diff options
Diffstat (limited to 'src/service/vpn')
-rw-r--r-- | src/service/vpn/.gitignore | 3 | ||||
-rw-r--r-- | src/service/vpn/Makefile.am | 53 | ||||
-rw-r--r-- | src/service/vpn/gnunet-helper-vpn.c | 707 | ||||
-rw-r--r-- | src/service/vpn/gnunet-service-vpn.c | 3130 | ||||
-rw-r--r-- | src/service/vpn/meson.build | 52 | ||||
-rw-r--r-- | src/service/vpn/tests/expected | bin | 0 -> 112 bytes | |||
-rw-r--r-- | src/service/vpn/tests/ping | bin | 0 -> 112 bytes | |||
-rwxr-xr-x | src/service/vpn/tests/test-helper-icmp.sh | 17 | ||||
-rwxr-xr-x | src/service/vpn/tests/test-helper-ifaddr.sh | 41 | ||||
-rw-r--r-- | src/service/vpn/vpn.conf.in | 19 | ||||
-rw-r--r-- | src/service/vpn/vpn.h | 153 | ||||
-rw-r--r-- | src/service/vpn/vpn_api.c | 529 |
12 files changed, 4704 insertions, 0 deletions
diff --git a/src/service/vpn/.gitignore b/src/service/vpn/.gitignore new file mode 100644 index 000000000..b76ea0f93 --- /dev/null +++ b/src/service/vpn/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | gnunet-vpn | ||
2 | gnunet-helper-vpn | ||
3 | gnunet-service-vpn | ||
diff --git a/src/service/vpn/Makefile.am b/src/service/vpn/Makefile.am new file mode 100644 index 000000000..af500a0b8 --- /dev/null +++ b/src/service/vpn/Makefile.am | |||
@@ -0,0 +1,53 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | endif | ||
7 | |||
8 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
9 | |||
10 | libexecdir= $(pkglibdir)/libexec/ | ||
11 | |||
12 | plugindir = $(libdir)/gnunet | ||
13 | |||
14 | pkgcfg_DATA = \ | ||
15 | vpn.conf | ||
16 | |||
17 | if LINUX | ||
18 | VPNBIN = gnunet-helper-vpn | ||
19 | endif | ||
20 | |||
21 | |||
22 | lib_LTLIBRARIES = \ | ||
23 | libgnunetvpn.la | ||
24 | |||
25 | |||
26 | libexec_PROGRAMS = \ | ||
27 | $(VPNBIN) \ | ||
28 | gnunet-service-vpn | ||
29 | |||
30 | gnunet_helper_vpn_SOURCES = \ | ||
31 | gnunet-helper-vpn.c | ||
32 | gnunet_helper_vpn_LDADD = \ | ||
33 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
34 | $(GN_LIBINTL) | ||
35 | |||
36 | gnunet_service_vpn_SOURCES = \ | ||
37 | gnunet-service-vpn.c | ||
38 | gnunet_service_vpn_LDADD = \ | ||
39 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
40 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
41 | $(top_builddir)/src/service/cadet/libgnunetcadet.la \ | ||
42 | $(top_builddir)/src/service/regex/libgnunetregex.la \ | ||
43 | $(GN_LIBINTL) | ||
44 | gnunet_service_vpn_CFLAGS = \ | ||
45 | -I$(top_srcdir)/src/service/exit $(CFLAGS) | ||
46 | |||
47 | libgnunetvpn_la_SOURCES = \ | ||
48 | vpn_api.c vpn.h | ||
49 | libgnunetvpn_la_LIBADD = \ | ||
50 | $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) | ||
51 | libgnunetvpn_la_LDFLAGS = \ | ||
52 | $(GN_LIBINTL) \ | ||
53 | $(GN_LIB_LDFLAGS) | ||
diff --git a/src/service/vpn/gnunet-helper-vpn.c b/src/service/vpn/gnunet-helper-vpn.c new file mode 100644 index 000000000..30901da45 --- /dev/null +++ b/src/service/vpn/gnunet-helper-vpn.c | |||
@@ -0,0 +1,707 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2012 Christian Grothoff | ||
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 | |||
21 | /** | ||
22 | * @file vpn/gnunet-helper-vpn.c | ||
23 | * @brief the helper for the VPN service. Opens a virtual network-interface, | ||
24 | * sends data received on the if to stdout, sends data received on stdin to the | ||
25 | * interface | ||
26 | * @author Philipp Tölke | ||
27 | * @author Christian Grothoff | ||
28 | * | ||
29 | * The following list of people have reviewed this code and considered | ||
30 | * it safe since the last modification (if you reviewed it, please | ||
31 | * have your name added to the list): | ||
32 | * | ||
33 | * - Philipp Tölke | ||
34 | */ | ||
35 | #include "platform.h" | ||
36 | #ifdef IF_TUN_HDR | ||
37 | #include IF_TUN_HDR | ||
38 | #endif | ||
39 | |||
40 | /** | ||
41 | * Need 'struct GNUNET_MessageHeader'. | ||
42 | */ | ||
43 | #include "gnunet_util_lib.h" | ||
44 | #include "gnunet_common.h" | ||
45 | |||
46 | /** | ||
47 | * Need VPN message types. | ||
48 | */ | ||
49 | #include "gnunet_protocols.h" | ||
50 | |||
51 | /** | ||
52 | * Should we print (interesting|debug) messages that can happen during | ||
53 | * normal operation? | ||
54 | */ | ||
55 | #define DEBUG GNUNET_NO | ||
56 | |||
57 | /** | ||
58 | * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE) | ||
59 | */ | ||
60 | #define MAX_SIZE 65536 | ||
61 | |||
62 | #if ! defined(__ANDROID__) | ||
63 | #ifndef _LINUX_IN6_H | ||
64 | /** | ||
65 | * This is in linux/include/net/ipv6.h, but not always exported... | ||
66 | */ | ||
67 | struct in6_ifreq | ||
68 | { | ||
69 | struct in6_addr ifr6_addr; | ||
70 | uint32_t ifr6_prefixlen; | ||
71 | unsigned int ifr6_ifindex; | ||
72 | }; | ||
73 | #endif | ||
74 | #endif | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Creates a tun-interface called dev; | ||
79 | * | ||
80 | * @param dev is assumed to point to a char[IFNAMSIZ] | ||
81 | * if *dev == '\\0', uses the name supplied by the kernel; | ||
82 | * @return the fd to the tun or -1 on error | ||
83 | */ | ||
84 | static int | ||
85 | init_tun (char *dev) | ||
86 | { | ||
87 | struct ifreq ifr; | ||
88 | int fd; | ||
89 | |||
90 | if (NULL == dev) | ||
91 | { | ||
92 | errno = EINVAL; | ||
93 | return -1; | ||
94 | } | ||
95 | |||
96 | if (-1 == (fd = open ("/dev/net/tun", O_RDWR))) | ||
97 | { | ||
98 | fprintf (stderr, | ||
99 | "Error opening `%s': %s\n", | ||
100 | "/dev/net/tun", | ||
101 | strerror (errno)); | ||
102 | return -1; | ||
103 | } | ||
104 | |||
105 | if (fd >= FD_SETSIZE) | ||
106 | { | ||
107 | fprintf (stderr, | ||
108 | "File descriptor to large: %d", | ||
109 | fd); | ||
110 | (void) close (fd); | ||
111 | return -1; | ||
112 | } | ||
113 | |||
114 | memset (&ifr, 0, sizeof(ifr)); | ||
115 | ifr.ifr_flags = IFF_TUN; | ||
116 | |||
117 | if ('\0' != *dev) | ||
118 | strncpy (ifr.ifr_name, | ||
119 | dev, | ||
120 | IFNAMSIZ); | ||
121 | |||
122 | if (-1 == ioctl (fd, | ||
123 | TUNSETIFF, | ||
124 | (void *) &ifr)) | ||
125 | { | ||
126 | fprintf (stderr, | ||
127 | "Error with ioctl on `%s': %s\n", | ||
128 | "/dev/net/tun", | ||
129 | strerror (errno)); | ||
130 | (void) close (fd); | ||
131 | return -1; | ||
132 | } | ||
133 | strcpy (dev, ifr.ifr_name); | ||
134 | return fd; | ||
135 | } | ||
136 | |||
137 | |||
138 | /** | ||
139 | * @brief Sets the IPv6-Address given in address on the interface dev | ||
140 | * | ||
141 | * @param dev the interface to configure | ||
142 | * @param address the IPv6-Address | ||
143 | * @param prefix_len the length of the network-prefix | ||
144 | */ | ||
145 | static void | ||
146 | set_address6 (const char *dev, | ||
147 | const char *address, | ||
148 | unsigned long prefix_len) | ||
149 | { | ||
150 | struct ifreq ifr; | ||
151 | struct in6_ifreq ifr6; | ||
152 | struct sockaddr_in6 sa6; | ||
153 | int fd; | ||
154 | |||
155 | /* | ||
156 | * parse the new address | ||
157 | */ | ||
158 | memset (&sa6, 0, sizeof(struct sockaddr_in6)); | ||
159 | sa6.sin6_family = AF_INET6; | ||
160 | if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr)) | ||
161 | { | ||
162 | fprintf (stderr, | ||
163 | "Failed to parse IPv6 address `%s'\n", | ||
164 | address); | ||
165 | exit (1); | ||
166 | } | ||
167 | |||
168 | if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0))) | ||
169 | { | ||
170 | fprintf (stderr, | ||
171 | "Error creating socket: %s\n", | ||
172 | strerror (errno)); | ||
173 | exit (1); | ||
174 | } | ||
175 | |||
176 | memset (&ifr, 0, sizeof(struct ifreq)); | ||
177 | /* | ||
178 | * Get the index of the if | ||
179 | */ | ||
180 | strncpy (ifr.ifr_name, | ||
181 | dev, | ||
182 | IFNAMSIZ); | ||
183 | if (-1 == ioctl (fd, | ||
184 | SIOGIFINDEX, | ||
185 | &ifr)) | ||
186 | { | ||
187 | fprintf (stderr, | ||
188 | "ioctl failed at %d: %s\n", | ||
189 | __LINE__, | ||
190 | strerror (errno)); | ||
191 | (void) close (fd); | ||
192 | exit (1); | ||
193 | } | ||
194 | |||
195 | memset (&ifr6, 0, sizeof(struct in6_ifreq)); | ||
196 | ifr6.ifr6_addr = sa6.sin6_addr; | ||
197 | ifr6.ifr6_ifindex = ifr.ifr_ifindex; | ||
198 | ifr6.ifr6_prefixlen = prefix_len; | ||
199 | |||
200 | /* | ||
201 | * Set the address | ||
202 | */ | ||
203 | if (-1 == ioctl (fd, | ||
204 | SIOCSIFADDR, | ||
205 | &ifr6)) | ||
206 | { | ||
207 | fprintf (stderr, | ||
208 | "ioctl failed at line %d: %s\n", | ||
209 | __LINE__, | ||
210 | strerror (errno)); | ||
211 | (void) close (fd); | ||
212 | exit (1); | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * Get the flags | ||
217 | */ | ||
218 | if (-1 == ioctl (fd, | ||
219 | SIOCGIFFLAGS, | ||
220 | &ifr)) | ||
221 | { | ||
222 | fprintf (stderr, | ||
223 | "ioctl failed at line %d: %s\n", | ||
224 | __LINE__, | ||
225 | strerror (errno)); | ||
226 | (void) close (fd); | ||
227 | exit (1); | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Add the UP and RUNNING flags | ||
232 | */ | ||
233 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | ||
234 | if (-1 == ioctl (fd, | ||
235 | SIOCSIFFLAGS, | ||
236 | &ifr)) | ||
237 | { | ||
238 | fprintf (stderr, | ||
239 | "ioctl failed at line %d: %s\n", | ||
240 | __LINE__, | ||
241 | strerror (errno)); | ||
242 | (void) close (fd); | ||
243 | exit (1); | ||
244 | } | ||
245 | |||
246 | if (0 != close (fd)) | ||
247 | { | ||
248 | fprintf (stderr, | ||
249 | "close failed at line %d: %s\n", | ||
250 | __LINE__, | ||
251 | strerror (errno)); | ||
252 | exit (1); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | |||
257 | /** | ||
258 | * @brief Sets the IPv4-Address given in address on the interface dev | ||
259 | * | ||
260 | * @param dev the interface to configure | ||
261 | * @param address the IPv4-Address | ||
262 | * @param mask the netmask | ||
263 | */ | ||
264 | static void | ||
265 | set_address4 (const char *dev, | ||
266 | const char *address, | ||
267 | const char *mask) | ||
268 | { | ||
269 | int fd; | ||
270 | struct sockaddr_in *addr; | ||
271 | struct ifreq ifr; | ||
272 | |||
273 | memset (&ifr, 0, sizeof(struct ifreq)); | ||
274 | addr = (struct sockaddr_in *) &(ifr.ifr_addr); | ||
275 | addr->sin_family = AF_INET; | ||
276 | |||
277 | /* | ||
278 | * Parse the address | ||
279 | */ | ||
280 | if (1 != inet_pton (AF_INET, | ||
281 | address, | ||
282 | &addr->sin_addr.s_addr)) | ||
283 | { | ||
284 | fprintf (stderr, | ||
285 | "Failed to parse IPv4 address `%s'\n", | ||
286 | address); | ||
287 | exit (1); | ||
288 | } | ||
289 | |||
290 | if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0))) | ||
291 | { | ||
292 | fprintf (stderr, | ||
293 | "Error creating socket: %s\n", | ||
294 | strerror (errno)); | ||
295 | exit (1); | ||
296 | } | ||
297 | |||
298 | strncpy (ifr.ifr_name, dev, IFNAMSIZ); | ||
299 | |||
300 | /* | ||
301 | * Set the address | ||
302 | */ | ||
303 | if (-1 == ioctl (fd, SIOCSIFADDR, &ifr)) | ||
304 | { | ||
305 | fprintf (stderr, | ||
306 | "ioctl failed at %d: %s\n", | ||
307 | __LINE__, | ||
308 | strerror (errno)); | ||
309 | (void) close (fd); | ||
310 | exit (1); | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * Parse the netmask | ||
315 | */ | ||
316 | addr = (struct sockaddr_in *) &(ifr.ifr_netmask); | ||
317 | if (1 != inet_pton (AF_INET, | ||
318 | mask, | ||
319 | &addr->sin_addr.s_addr)) | ||
320 | { | ||
321 | fprintf (stderr, | ||
322 | "Failed to parse IPv4 address mask `%s'\n", | ||
323 | mask); | ||
324 | (void) close (fd); | ||
325 | exit (1); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Set the netmask | ||
330 | */ | ||
331 | if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr)) | ||
332 | { | ||
333 | fprintf (stderr, | ||
334 | "ioctl failed at line %d: %s\n", | ||
335 | __LINE__, | ||
336 | strerror (errno)); | ||
337 | (void) close (fd); | ||
338 | exit (1); | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * Get the flags | ||
343 | */ | ||
344 | if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr)) | ||
345 | { | ||
346 | fprintf (stderr, | ||
347 | "ioctl failed at line %d: %s\n", | ||
348 | __LINE__, | ||
349 | strerror (errno)); | ||
350 | (void) close (fd); | ||
351 | exit (1); | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * Add the UP and RUNNING flags | ||
356 | */ | ||
357 | ifr.ifr_flags |= IFF_UP | IFF_RUNNING; | ||
358 | if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr)) | ||
359 | { | ||
360 | fprintf (stderr, | ||
361 | "ioctl failed at line %d: %s\n", | ||
362 | __LINE__, | ||
363 | strerror (errno)); | ||
364 | (void) close (fd); | ||
365 | exit (1); | ||
366 | } | ||
367 | |||
368 | if (0 != close (fd)) | ||
369 | { | ||
370 | fprintf (stderr, | ||
371 | "close failed at line %d: %s\n", | ||
372 | __LINE__, | ||
373 | strerror (errno)); | ||
374 | exit (1); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | |||
379 | /** | ||
380 | * Start forwarding to and from the tunnel. | ||
381 | * | ||
382 | * @param fd_tun tunnel FD | ||
383 | */ | ||
384 | static void | ||
385 | run (int fd_tun) | ||
386 | { | ||
387 | /* | ||
388 | * The buffer filled by reading from fd_tun | ||
389 | */ | ||
390 | unsigned char buftun[MAX_SIZE]; | ||
391 | ssize_t buftun_size = 0; | ||
392 | unsigned char *buftun_read = NULL; | ||
393 | |||
394 | /* | ||
395 | * The buffer filled by reading from stdin | ||
396 | */ | ||
397 | unsigned char bufin[MAX_SIZE]; | ||
398 | ssize_t bufin_size = 0; | ||
399 | size_t bufin_rpos = 0; | ||
400 | unsigned char *bufin_read = NULL; | ||
401 | |||
402 | fd_set fds_w; | ||
403 | fd_set fds_r; | ||
404 | |||
405 | /* read refers to reading from fd_tun, writing to stdout */ | ||
406 | int read_open = 1; | ||
407 | |||
408 | /* write refers to reading from stdin, writing to fd_tun */ | ||
409 | int write_open = 1; | ||
410 | |||
411 | while ((1 == read_open) && (1 == write_open)) | ||
412 | { | ||
413 | FD_ZERO (&fds_w); | ||
414 | FD_ZERO (&fds_r); | ||
415 | |||
416 | /* | ||
417 | * We are supposed to read and the buffer is empty | ||
418 | * -> select on read from tun | ||
419 | */ | ||
420 | if (read_open && (0 == buftun_size)) | ||
421 | FD_SET (fd_tun, &fds_r); | ||
422 | |||
423 | /* | ||
424 | * We are supposed to read and the buffer is not empty | ||
425 | * -> select on write to stdout | ||
426 | */ | ||
427 | if (read_open && (0 != buftun_size)) | ||
428 | FD_SET (1, &fds_w); | ||
429 | |||
430 | /* | ||
431 | * We are supposed to write and the buffer is empty | ||
432 | * -> select on read from stdin | ||
433 | */ | ||
434 | if (write_open && (NULL == bufin_read)) | ||
435 | FD_SET (0, &fds_r); | ||
436 | |||
437 | /* | ||
438 | * We are supposed to write and the buffer is not empty | ||
439 | * -> select on write to tun | ||
440 | */ | ||
441 | if (write_open && (NULL != bufin_read)) | ||
442 | FD_SET (fd_tun, &fds_w); | ||
443 | |||
444 | int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL); | ||
445 | |||
446 | if (-1 == r) | ||
447 | { | ||
448 | if (EINTR == errno) | ||
449 | continue; | ||
450 | fprintf (stderr, | ||
451 | "select failed: %s\n", | ||
452 | strerror (errno)); | ||
453 | exit (1); | ||
454 | } | ||
455 | |||
456 | if (r > 0) | ||
457 | { | ||
458 | if (FD_ISSET (fd_tun, &fds_r)) | ||
459 | { | ||
460 | buftun_size = | ||
461 | read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader), | ||
462 | MAX_SIZE - sizeof(struct GNUNET_MessageHeader)); | ||
463 | if (-1 == buftun_size) | ||
464 | { | ||
465 | fprintf (stderr, | ||
466 | "read-error: %s\n", | ||
467 | strerror (errno)); | ||
468 | shutdown (fd_tun, SHUT_RD); | ||
469 | shutdown (1, SHUT_WR); | ||
470 | read_open = 0; | ||
471 | buftun_size = 0; | ||
472 | } | ||
473 | else if (0 == buftun_size) | ||
474 | { | ||
475 | fprintf (stderr, "EOF on tun\n"); | ||
476 | shutdown (fd_tun, SHUT_RD); | ||
477 | shutdown (1, SHUT_WR); | ||
478 | read_open = 0; | ||
479 | buftun_size = 0; | ||
480 | } | ||
481 | else | ||
482 | { | ||
483 | buftun_read = buftun; | ||
484 | struct GNUNET_MessageHeader *hdr = | ||
485 | (struct GNUNET_MessageHeader *) buftun; | ||
486 | buftun_size += sizeof(struct GNUNET_MessageHeader); | ||
487 | hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
488 | hdr->size = htons (buftun_size); | ||
489 | } | ||
490 | } | ||
491 | else if (FD_ISSET (1, &fds_w)) | ||
492 | { | ||
493 | ssize_t written = write (1, | ||
494 | buftun_read, | ||
495 | buftun_size); | ||
496 | |||
497 | if (-1 == written) | ||
498 | { | ||
499 | #if ! DEBUG | ||
500 | if (errno != EPIPE) | ||
501 | #endif | ||
502 | fprintf (stderr, | ||
503 | "write-error to stdout: %s\n", | ||
504 | strerror (errno)); | ||
505 | shutdown (fd_tun, SHUT_RD); | ||
506 | shutdown (1, SHUT_WR); | ||
507 | read_open = 0; | ||
508 | buftun_size = 0; | ||
509 | } | ||
510 | else if (0 == written) | ||
511 | { | ||
512 | fprintf (stderr, | ||
513 | "write returned 0!?\n"); | ||
514 | exit (1); | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | buftun_size -= written; | ||
519 | buftun_read += written; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | if (FD_ISSET (0, &fds_r)) | ||
524 | { | ||
525 | bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos); | ||
526 | if (-1 == bufin_size) | ||
527 | { | ||
528 | fprintf (stderr, | ||
529 | "read-error: %s\n", | ||
530 | strerror (errno)); | ||
531 | shutdown (0, SHUT_RD); | ||
532 | shutdown (fd_tun, SHUT_WR); | ||
533 | write_open = 0; | ||
534 | bufin_size = 0; | ||
535 | } | ||
536 | else if (0 == bufin_size) | ||
537 | { | ||
538 | #if DEBUG | ||
539 | fprintf (stderr, "EOF on stdin\n"); | ||
540 | #endif | ||
541 | shutdown (0, SHUT_RD); | ||
542 | shutdown (fd_tun, SHUT_WR); | ||
543 | write_open = 0; | ||
544 | bufin_size = 0; | ||
545 | } | ||
546 | else | ||
547 | { | ||
548 | struct GNUNET_MessageHeader *hdr; | ||
549 | |||
550 | PROCESS_BUFFER: | ||
551 | bufin_rpos += bufin_size; | ||
552 | if (bufin_rpos < sizeof(struct GNUNET_MessageHeader)) | ||
553 | continue; | ||
554 | hdr = (struct GNUNET_MessageHeader *) bufin; | ||
555 | if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) | ||
556 | { | ||
557 | fprintf (stderr, | ||
558 | "protocol violation!\n"); | ||
559 | exit (1); | ||
560 | } | ||
561 | if (ntohs (hdr->size) > bufin_rpos) | ||
562 | continue; | ||
563 | bufin_read = bufin + sizeof(struct GNUNET_MessageHeader); | ||
564 | bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader); | ||
565 | bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader); | ||
566 | } | ||
567 | } | ||
568 | else if (FD_ISSET (fd_tun, &fds_w)) | ||
569 | { | ||
570 | ssize_t written = write (fd_tun, | ||
571 | bufin_read, | ||
572 | bufin_size); | ||
573 | |||
574 | if (-1 == written) | ||
575 | { | ||
576 | fprintf (stderr, | ||
577 | "write-error to tun: %s\n", | ||
578 | strerror (errno)); | ||
579 | shutdown (0, SHUT_RD); | ||
580 | shutdown (fd_tun, SHUT_WR); | ||
581 | write_open = 0; | ||
582 | bufin_size = 0; | ||
583 | } | ||
584 | else if (0 == written) | ||
585 | { | ||
586 | fprintf (stderr, "write returned 0!?\n"); | ||
587 | exit (1); | ||
588 | } | ||
589 | else | ||
590 | { | ||
591 | bufin_size -= written; | ||
592 | bufin_read += written; | ||
593 | if (0 == bufin_size) | ||
594 | { | ||
595 | memmove (bufin, bufin_read, bufin_rpos); | ||
596 | bufin_read = NULL; /* start reading again */ | ||
597 | bufin_size = 0; | ||
598 | goto PROCESS_BUFFER; | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | } | ||
605 | |||
606 | |||
607 | /** | ||
608 | * Open VPN tunnel interface. | ||
609 | * | ||
610 | * @param argc must be 6 | ||
611 | * @param argv 0: binary name (gnunet-helper-vpn) | ||
612 | * 1: tunnel interface name (gnunet-vpn) | ||
613 | * 2: IPv6 address (::1), "-" to disable | ||
614 | * 3: IPv6 netmask length in bits (64), ignored if #2 is "-" | ||
615 | * 4: IPv4 address (1.2.3.4), "-" to disable | ||
616 | * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-" | ||
617 | */ | ||
618 | int | ||
619 | main (int argc, char **argv) | ||
620 | { | ||
621 | char dev[IFNAMSIZ]; | ||
622 | int fd_tun; | ||
623 | int global_ret; | ||
624 | |||
625 | if (6 != argc) | ||
626 | { | ||
627 | fprintf (stderr, "Fatal: must supply 5 arguments!\n"); | ||
628 | return 1; | ||
629 | } | ||
630 | |||
631 | strncpy (dev, | ||
632 | argv[1], | ||
633 | IFNAMSIZ); | ||
634 | dev[IFNAMSIZ - 1] = '\0'; | ||
635 | |||
636 | if (-1 == (fd_tun = init_tun (dev))) | ||
637 | { | ||
638 | fprintf (stderr, | ||
639 | "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n", | ||
640 | dev, | ||
641 | argv[2], | ||
642 | argv[3], | ||
643 | argv[4], | ||
644 | argv[5]); | ||
645 | return 1; | ||
646 | } | ||
647 | |||
648 | if (0 != strcmp (argv[2], "-")) | ||
649 | { | ||
650 | const char *address = argv[2]; | ||
651 | long prefix_len = atol (argv[3]); | ||
652 | |||
653 | if ((prefix_len < 1) || (prefix_len > 127)) | ||
654 | { | ||
655 | fprintf (stderr, | ||
656 | "Fatal: prefix_len out of range\n"); | ||
657 | (void) close (fd_tun); | ||
658 | return 1; | ||
659 | } | ||
660 | |||
661 | set_address6 (dev, | ||
662 | address, | ||
663 | prefix_len); | ||
664 | } | ||
665 | |||
666 | if (0 != strcmp (argv[4], "-")) | ||
667 | { | ||
668 | const char *address = argv[4]; | ||
669 | const char *mask = argv[5]; | ||
670 | |||
671 | set_address4 (dev, address, mask); | ||
672 | } | ||
673 | |||
674 | uid_t uid = getuid (); | ||
675 | #ifdef HAVE_SETRESUID | ||
676 | if (0 != setresuid (uid, uid, uid)) | ||
677 | { | ||
678 | fprintf (stderr, | ||
679 | "Failed to setresuid: %s\n", | ||
680 | strerror (errno)); | ||
681 | global_ret = 2; | ||
682 | goto cleanup; | ||
683 | } | ||
684 | #else | ||
685 | if (0 != (setuid (uid) | seteuid (uid))) | ||
686 | { | ||
687 | fprintf (stderr, | ||
688 | "Failed to setuid: %s\n", | ||
689 | strerror (errno)); | ||
690 | global_ret = 2; | ||
691 | goto cleanup; | ||
692 | } | ||
693 | #endif | ||
694 | |||
695 | if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) | ||
696 | { | ||
697 | fprintf (stderr, | ||
698 | "Failed to protect against SIGPIPE: %s\n", | ||
699 | strerror (errno)); | ||
700 | /* no exit, we might as well die with SIGPIPE should it ever happen */ | ||
701 | } | ||
702 | run (fd_tun); | ||
703 | global_ret = 0; | ||
704 | cleanup: | ||
705 | (void) close (fd_tun); | ||
706 | return global_ret; | ||
707 | } | ||
diff --git a/src/service/vpn/gnunet-service-vpn.c b/src/service/vpn/gnunet-service-vpn.c new file mode 100644 index 000000000..aa3f6ffe3 --- /dev/null +++ b/src/service/vpn/gnunet-service-vpn.c | |||
@@ -0,0 +1,3130 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2011, 2012, 2016, 2017 Christian Grothoff | ||
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 | |||
21 | /** | ||
22 | * @file vpn/gnunet-service-vpn.c | ||
23 | * @brief service that opens a virtual interface and allows its clients | ||
24 | * to allocate IPs on the virtual interface and to then redirect | ||
25 | * IP traffic received on those IPs via the GNUnet cadet | ||
26 | * @author Philipp Toelke | ||
27 | * @author Christian Grothoff | ||
28 | * | ||
29 | * TODO: | ||
30 | * - keep multiple peers/cadet channels ready as alternative exits / | ||
31 | * detect & recover from channel-to-exit failure gracefully | ||
32 | */ | ||
33 | #include "platform.h" | ||
34 | #include "gnunet_util_lib.h" | ||
35 | #include "gnunet_common.h" | ||
36 | #include "gnunet_protocols.h" | ||
37 | #include "gnunet_applications.h" | ||
38 | #include "gnunet_cadet_service.h" | ||
39 | #include "gnunet_statistics_service.h" | ||
40 | #include "gnunet_constants.h" | ||
41 | #include "gnunet_regex_service.h" | ||
42 | #include "vpn.h" | ||
43 | #include "exit.h" | ||
44 | |||
45 | |||
46 | /** | ||
47 | * Maximum number of messages we allow in the queue for cadet. | ||
48 | */ | ||
49 | #define MAX_MESSAGE_QUEUE_SIZE 4 | ||
50 | |||
51 | |||
52 | /** | ||
53 | * State we keep for each of our channels. | ||
54 | */ | ||
55 | struct ChannelState; | ||
56 | |||
57 | /** | ||
58 | * Information we track for each IP address to determine which channel | ||
59 | * to send the traffic over to the destination. | ||
60 | */ | ||
61 | struct DestinationEntry; | ||
62 | |||
63 | /** | ||
64 | * List of channels we keep for each destination port for a given | ||
65 | * destination entry. | ||
66 | */ | ||
67 | struct DestinationChannel | ||
68 | { | ||
69 | /** | ||
70 | * Kept in a DLL. | ||
71 | */ | ||
72 | struct DestinationChannel *next; | ||
73 | |||
74 | /** | ||
75 | * Kept in a DLL. | ||
76 | */ | ||
77 | struct DestinationChannel *prev; | ||
78 | |||
79 | /** | ||
80 | * Destination entry list this `struct DestinationChannel` belongs with. | ||
81 | */ | ||
82 | struct DestinationEntry *destination; | ||
83 | |||
84 | /** | ||
85 | * Destination port this channel state is used for. | ||
86 | */ | ||
87 | uint16_t destination_port; | ||
88 | }; | ||
89 | |||
90 | |||
91 | /** | ||
92 | * Information we track for each IP address to determine which channel | ||
93 | * to send the traffic over to the destination. | ||
94 | */ | ||
95 | struct DestinationEntry | ||
96 | { | ||
97 | /** | ||
98 | * Key under which this entry is in the 'destination_map' (only valid | ||
99 | * if 'heap_node != NULL'). | ||
100 | */ | ||
101 | struct GNUNET_HashCode key; | ||
102 | |||
103 | /** | ||
104 | * Head of DLL of channels associated with this destination. | ||
105 | */ | ||
106 | struct DestinationChannel *dt_head; | ||
107 | |||
108 | /** | ||
109 | * Tail of DLL of channels associated with this destination. | ||
110 | */ | ||
111 | struct DestinationChannel *dt_tail; | ||
112 | |||
113 | /** | ||
114 | * Entry for this entry in the destination_heap. | ||
115 | */ | ||
116 | struct GNUNET_CONTAINER_HeapNode *heap_node; | ||
117 | |||
118 | /** | ||
119 | * #GNUNET_NO if this is a channel to an Internet-exit, | ||
120 | * #GNUNET_YES if this channel is to a service. | ||
121 | */ | ||
122 | int is_service; | ||
123 | |||
124 | /** | ||
125 | * Details about the connection (depending on is_service). | ||
126 | */ | ||
127 | union | ||
128 | { | ||
129 | struct | ||
130 | { | ||
131 | /** | ||
132 | * The description of the service (only used for service channels). | ||
133 | */ | ||
134 | struct GNUNET_HashCode service_descriptor; | ||
135 | |||
136 | /** | ||
137 | * Peer offering the service. | ||
138 | */ | ||
139 | struct GNUNET_PeerIdentity target; | ||
140 | } service_destination; | ||
141 | |||
142 | struct | ||
143 | { | ||
144 | /** | ||
145 | * Address family used (AF_INET or AF_INET6). | ||
146 | */ | ||
147 | int af; | ||
148 | |||
149 | /** | ||
150 | * IP address of the ultimate destination (only used for exit channels). | ||
151 | */ | ||
152 | union | ||
153 | { | ||
154 | /** | ||
155 | * Address if af is AF_INET. | ||
156 | */ | ||
157 | struct in_addr v4; | ||
158 | |||
159 | /** | ||
160 | * Address if af is AF_INET6. | ||
161 | */ | ||
162 | struct in6_addr v6; | ||
163 | } ip; | ||
164 | } exit_destination; | ||
165 | } details; | ||
166 | }; | ||
167 | |||
168 | |||
169 | /** | ||
170 | * A messages we have in queue for a particular channel. | ||
171 | */ | ||
172 | struct ChannelMessageQueueEntry | ||
173 | { | ||
174 | /** | ||
175 | * This is a doubly-linked list. | ||
176 | */ | ||
177 | struct ChannelMessageQueueEntry *next; | ||
178 | |||
179 | /** | ||
180 | * This is a doubly-linked list. | ||
181 | */ | ||
182 | struct ChannelMessageQueueEntry *prev; | ||
183 | |||
184 | /** | ||
185 | * Number of bytes in @e msg. | ||
186 | */ | ||
187 | size_t len; | ||
188 | |||
189 | /** | ||
190 | * Message to transmit, allocated at the end of this struct. | ||
191 | */ | ||
192 | const void *msg; | ||
193 | }; | ||
194 | |||
195 | |||
196 | /** | ||
197 | * State we keep for each of our channels. | ||
198 | */ | ||
199 | struct ChannelState | ||
200 | { | ||
201 | /** | ||
202 | * Information about the channel to use, NULL if no channel | ||
203 | * is available right now. | ||
204 | */ | ||
205 | struct GNUNET_CADET_Channel *channel; | ||
206 | |||
207 | /** | ||
208 | * Active query with REGEX to locate exit. | ||
209 | */ | ||
210 | struct GNUNET_REGEX_Search *search; | ||
211 | |||
212 | /** | ||
213 | * Entry for this entry in the channel_heap, NULL as long as this | ||
214 | * channel state is not fully bound. | ||
215 | */ | ||
216 | struct GNUNET_CONTAINER_HeapNode *heap_node; | ||
217 | |||
218 | /** | ||
219 | * Head of list of messages scheduled for transmission. | ||
220 | */ | ||
221 | struct ChannelMessageQueueEntry *tmq_head; | ||
222 | |||
223 | /** | ||
224 | * Tail of list of messages scheduled for transmission. | ||
225 | */ | ||
226 | struct ChannelMessageQueueEntry *tmq_tail; | ||
227 | |||
228 | /** | ||
229 | * Destination to which this channel leads. Note that | ||
230 | * this struct is NOT in the destination_map (but a | ||
231 | * local copy) and that the 'heap_node' should always | ||
232 | * be NULL. | ||
233 | */ | ||
234 | struct DestinationEntry destination; | ||
235 | |||
236 | /** | ||
237 | * Address family used for this channel on the local TUN interface. | ||
238 | */ | ||
239 | int af; | ||
240 | |||
241 | /** | ||
242 | * Is this channel new (#GNUNET_NO), or did we exchange messages with the | ||
243 | * other side already (#GNUNET_YES)? | ||
244 | */ | ||
245 | int is_established; | ||
246 | |||
247 | /** | ||
248 | * Length of the doubly linked 'tmq_head/tmq_tail' list. | ||
249 | */ | ||
250 | unsigned int tmq_length; | ||
251 | |||
252 | /** | ||
253 | * IPPROTO_TCP or IPPROTO_UDP once bound. | ||
254 | */ | ||
255 | uint8_t protocol; | ||
256 | |||
257 | /** | ||
258 | * IP address of the source on our end, initially uninitialized. | ||
259 | */ | ||
260 | union | ||
261 | { | ||
262 | /** | ||
263 | * Address if af is AF_INET. | ||
264 | */ | ||
265 | struct in_addr v4; | ||
266 | |||
267 | /** | ||
268 | * Address if af is AF_INET6. | ||
269 | */ | ||
270 | struct in6_addr v6; | ||
271 | } source_ip; | ||
272 | |||
273 | /** | ||
274 | * Destination IP address used by the source on our end (this is the IP | ||
275 | * that we pick freely within the VPN's channel IP range). | ||
276 | */ | ||
277 | union | ||
278 | { | ||
279 | /** | ||
280 | * Address if af is AF_INET. | ||
281 | */ | ||
282 | struct in_addr v4; | ||
283 | |||
284 | /** | ||
285 | * Address if af is AF_INET6. | ||
286 | */ | ||
287 | struct in6_addr v6; | ||
288 | } destination_ip; | ||
289 | |||
290 | /** | ||
291 | * Source port used by the sender on our end; 0 for uninitialized. | ||
292 | */ | ||
293 | uint16_t source_port; | ||
294 | |||
295 | /** | ||
296 | * Destination port used by the sender on our end; 0 for uninitialized. | ||
297 | */ | ||
298 | uint16_t destination_port; | ||
299 | }; | ||
300 | |||
301 | |||
302 | /** | ||
303 | * Return value from #main(). | ||
304 | */ | ||
305 | static int global_ret; | ||
306 | |||
307 | /** | ||
308 | * Configuration we use. | ||
309 | */ | ||
310 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
311 | |||
312 | /** | ||
313 | * Handle to the cadet service. | ||
314 | */ | ||
315 | static struct GNUNET_CADET_Handle *cadet_handle; | ||
316 | |||
317 | /** | ||
318 | * Map from IP address to destination information (possibly with a | ||
319 | * CADET channel handle for fast setup). | ||
320 | */ | ||
321 | static struct GNUNET_CONTAINER_MultiHashMap *destination_map; | ||
322 | |||
323 | /** | ||
324 | * Min-Heap sorted by activity time to expire old mappings. | ||
325 | */ | ||
326 | static struct GNUNET_CONTAINER_Heap *destination_heap; | ||
327 | |||
328 | /** | ||
329 | * Map from source and destination address (IP+port) to connection | ||
330 | * information (mostly with the respective CADET channel handle). | ||
331 | */ | ||
332 | static struct GNUNET_CONTAINER_MultiHashMap *channel_map; | ||
333 | |||
334 | /** | ||
335 | * Min-Heap sorted by activity time to expire old mappings; values are | ||
336 | * of type 'struct ChannelState'. | ||
337 | */ | ||
338 | static struct GNUNET_CONTAINER_Heap *channel_heap; | ||
339 | |||
340 | /** | ||
341 | * Statistics. | ||
342 | */ | ||
343 | static struct GNUNET_STATISTICS_Handle *stats; | ||
344 | |||
345 | /** | ||
346 | * The handle to the VPN helper process "gnunet-helper-vpn". | ||
347 | */ | ||
348 | static struct GNUNET_HELPER_Handle *helper_handle; | ||
349 | |||
350 | /** | ||
351 | * Arguments to the vpn helper. | ||
352 | */ | ||
353 | static char *vpn_argv[7]; | ||
354 | |||
355 | /** | ||
356 | * Length of the prefix of the VPN's IPv6 network. | ||
357 | */ | ||
358 | static unsigned long long ipv6prefix; | ||
359 | |||
360 | /** | ||
361 | * If there are more than this number of address-mappings, old ones | ||
362 | * will be removed | ||
363 | */ | ||
364 | static unsigned long long max_destination_mappings; | ||
365 | |||
366 | /** | ||
367 | * If there are more than this number of open channels, old ones | ||
368 | * will be removed | ||
369 | */ | ||
370 | static unsigned long long max_channel_mappings; | ||
371 | |||
372 | |||
373 | /** | ||
374 | * Compute the key under which we would store an entry in the | ||
375 | * #destination_map for the given IP address. | ||
376 | * | ||
377 | * @param af address family (AF_INET or AF_INET6) | ||
378 | * @param address IP address, struct in_addr or struct in6_addr | ||
379 | * @param key where to store the key | ||
380 | */ | ||
381 | static void | ||
382 | get_destination_key_from_ip (int af, | ||
383 | const void *address, | ||
384 | struct GNUNET_HashCode *key) | ||
385 | { | ||
386 | switch (af) | ||
387 | { | ||
388 | case AF_INET: | ||
389 | GNUNET_CRYPTO_hash (address, sizeof(struct in_addr), key); | ||
390 | break; | ||
391 | |||
392 | case AF_INET6: | ||
393 | GNUNET_CRYPTO_hash (address, sizeof(struct in6_addr), key); | ||
394 | break; | ||
395 | |||
396 | default: | ||
397 | GNUNET_assert (0); | ||
398 | break; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | |||
403 | /** | ||
404 | * Compute the key under which we would store an entry in the | ||
405 | * channel_map for the given socket address pair. | ||
406 | * | ||
407 | * @param af address family (AF_INET or AF_INET6) | ||
408 | * @param protocol IPPROTO_TCP or IPPROTO_UDP | ||
409 | * @param source_ip sender's source IP, struct in_addr or struct in6_addr | ||
410 | * @param source_port sender's source port | ||
411 | * @param destination_ip sender's destination IP, struct in_addr or struct in6_addr | ||
412 | * @param destination_port sender's destination port | ||
413 | * @param key where to store the key | ||
414 | */ | ||
415 | static void | ||
416 | get_channel_key_from_ips (int af, | ||
417 | uint8_t protocol, | ||
418 | const void *source_ip, | ||
419 | uint16_t source_port, | ||
420 | const void *destination_ip, | ||
421 | uint16_t destination_port, | ||
422 | struct GNUNET_HashCode *key) | ||
423 | { | ||
424 | char *off; | ||
425 | |||
426 | memset (key, 0, sizeof(struct GNUNET_HashCode)); | ||
427 | /* the GNUnet hashmap only uses the first sizeof(unsigned int) of the hash, | ||
428 | so we put the ports in there (and hope for few collisions) */ | ||
429 | off = (char *) key; | ||
430 | GNUNET_memcpy (off, &source_port, sizeof(uint16_t)); | ||
431 | off += sizeof(uint16_t); | ||
432 | GNUNET_memcpy (off, &destination_port, sizeof(uint16_t)); | ||
433 | off += sizeof(uint16_t); | ||
434 | switch (af) | ||
435 | { | ||
436 | case AF_INET: | ||
437 | GNUNET_memcpy (off, source_ip, sizeof(struct in_addr)); | ||
438 | off += sizeof(struct in_addr); | ||
439 | GNUNET_memcpy (off, destination_ip, sizeof(struct in_addr)); | ||
440 | off += sizeof(struct in_addr); | ||
441 | break; | ||
442 | |||
443 | case AF_INET6: | ||
444 | GNUNET_memcpy (off, source_ip, sizeof(struct in6_addr)); | ||
445 | off += sizeof(struct in6_addr); | ||
446 | GNUNET_memcpy (off, destination_ip, sizeof(struct in6_addr)); | ||
447 | off += sizeof(struct in6_addr); | ||
448 | break; | ||
449 | |||
450 | default: | ||
451 | GNUNET_assert (0); | ||
452 | break; | ||
453 | } | ||
454 | GNUNET_memcpy (off, &protocol, sizeof(uint8_t)); | ||
455 | /* off += sizeof (uint8_t); */ | ||
456 | } | ||
457 | |||
458 | |||
459 | /** | ||
460 | * Notify the client about the result of its request. | ||
461 | * | ||
462 | * @param client client to notify | ||
463 | * @param request_id original request ID to include in response | ||
464 | * @param result_af resulting address family | ||
465 | * @param addr resulting IP address | ||
466 | */ | ||
467 | static void | ||
468 | send_client_reply (struct GNUNET_SERVICE_Client *client, | ||
469 | uint64_t request_id, | ||
470 | int result_af, | ||
471 | const void *addr) | ||
472 | { | ||
473 | struct GNUNET_MQ_Envelope *env; | ||
474 | struct RedirectToIpResponseMessage *res; | ||
475 | size_t rlen; | ||
476 | |||
477 | switch (result_af) | ||
478 | { | ||
479 | case AF_INET: | ||
480 | rlen = sizeof(struct in_addr); | ||
481 | break; | ||
482 | |||
483 | case AF_INET6: | ||
484 | rlen = sizeof(struct in6_addr); | ||
485 | break; | ||
486 | |||
487 | case AF_UNSPEC: | ||
488 | rlen = 0; | ||
489 | break; | ||
490 | |||
491 | default: | ||
492 | GNUNET_assert (0); | ||
493 | return; | ||
494 | } | ||
495 | env = GNUNET_MQ_msg_extra (res, rlen, GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP); | ||
496 | res->result_af = htonl (result_af); | ||
497 | res->request_id = request_id; | ||
498 | GNUNET_memcpy (&res[1], addr, rlen); | ||
499 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env); | ||
500 | } | ||
501 | |||
502 | |||
503 | /** | ||
504 | * Free resources associated with a channel state. | ||
505 | * | ||
506 | * @param ts state to free | ||
507 | */ | ||
508 | static void | ||
509 | free_channel_state (struct ChannelState *ts) | ||
510 | { | ||
511 | struct GNUNET_HashCode key; | ||
512 | struct ChannelMessageQueueEntry *tnq; | ||
513 | struct GNUNET_CADET_Channel *channel; | ||
514 | |||
515 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up channel state\n"); | ||
516 | if (NULL != (channel = ts->channel)) | ||
517 | { | ||
518 | ts->channel = NULL; | ||
519 | GNUNET_CADET_channel_destroy (channel); | ||
520 | return; | ||
521 | } | ||
522 | GNUNET_STATISTICS_update (stats, | ||
523 | gettext_noop ("# Active channels"), | ||
524 | -1, | ||
525 | GNUNET_NO); | ||
526 | while (NULL != (tnq = ts->tmq_head)) | ||
527 | { | ||
528 | GNUNET_CONTAINER_DLL_remove (ts->tmq_head, ts->tmq_tail, tnq); | ||
529 | ts->tmq_length--; | ||
530 | GNUNET_free (tnq); | ||
531 | } | ||
532 | GNUNET_assert (0 == ts->tmq_length); | ||
533 | GNUNET_assert (NULL == ts->destination.heap_node); | ||
534 | if (NULL != ts->search) | ||
535 | { | ||
536 | GNUNET_REGEX_search_cancel (ts->search); | ||
537 | ts->search = NULL; | ||
538 | } | ||
539 | if (NULL != ts->heap_node) | ||
540 | { | ||
541 | GNUNET_CONTAINER_heap_remove_node (ts->heap_node); | ||
542 | ts->heap_node = NULL; | ||
543 | get_channel_key_from_ips (ts->af, | ||
544 | ts->protocol, | ||
545 | &ts->source_ip, | ||
546 | ts->source_port, | ||
547 | &ts->destination_ip, | ||
548 | ts->destination_port, | ||
549 | &key); | ||
550 | GNUNET_assert ( | ||
551 | GNUNET_YES == | ||
552 | GNUNET_CONTAINER_multihashmap_remove (channel_map, &key, ts)); | ||
553 | } | ||
554 | GNUNET_free (ts); | ||
555 | } | ||
556 | |||
557 | |||
558 | /** | ||
559 | * Add the given message to the given channel and trigger the | ||
560 | * transmission process. | ||
561 | * | ||
562 | * @param ts channel to queue the message for | ||
563 | * @param env message to queue | ||
564 | */ | ||
565 | static void | ||
566 | send_to_channel (struct ChannelState *ts, struct GNUNET_MQ_Envelope *env) | ||
567 | { | ||
568 | struct GNUNET_MQ_Handle *mq; | ||
569 | |||
570 | GNUNET_assert (NULL != ts->channel); | ||
571 | mq = GNUNET_CADET_get_mq (ts->channel); | ||
572 | GNUNET_MQ_env_set_options (env, | ||
573 | GNUNET_MQ_PRIO_BEST_EFFORT | ||
574 | | GNUNET_MQ_PREF_OUT_OF_ORDER); | ||
575 | GNUNET_MQ_send (mq, env); | ||
576 | if (GNUNET_MQ_get_length (mq) > MAX_MESSAGE_QUEUE_SIZE) | ||
577 | { | ||
578 | env = GNUNET_MQ_unsent_head (mq); | ||
579 | GNUNET_assert (NULL != env); | ||
580 | GNUNET_STATISTICS_update (stats, | ||
581 | gettext_noop ( | ||
582 | "# Messages dropped in cadet queue (overflow)"), | ||
583 | 1, | ||
584 | GNUNET_NO); | ||
585 | GNUNET_MQ_discard (env); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | |||
590 | /** | ||
591 | * Output destination of a channel for diagnostics. | ||
592 | * | ||
593 | * @param de destination to process | ||
594 | * @return diagnostic string describing destination | ||
595 | */ | ||
596 | static const char * | ||
597 | print_channel_destination (const struct DestinationEntry *de) | ||
598 | { | ||
599 | static char dest[256]; | ||
600 | |||
601 | if (de->is_service) | ||
602 | { | ||
603 | GNUNET_snprintf (dest, | ||
604 | sizeof(dest), | ||
605 | "HS: %s-%s", | ||
606 | GNUNET_i2s (&de->details.service_destination.target), | ||
607 | GNUNET_h2s ( | ||
608 | &de->details.service_destination.service_descriptor)); | ||
609 | } | ||
610 | else | ||
611 | { | ||
612 | inet_ntop (de->details.exit_destination.af, | ||
613 | &de->details.exit_destination.ip, | ||
614 | dest, | ||
615 | sizeof(dest)); | ||
616 | } | ||
617 | return dest; | ||
618 | } | ||
619 | |||
620 | |||
621 | /** | ||
622 | * Function called whenever a channel is destroyed. Should clean up | ||
623 | * any associated state. | ||
624 | * | ||
625 | * @param cls our `struct ChannelState` | ||
626 | * @param channel connection to the other end (henceforth invalid) | ||
627 | */ | ||
628 | static void | ||
629 | channel_cleaner (void *cls, const struct GNUNET_CADET_Channel *channel) | ||
630 | { | ||
631 | struct ChannelState *ts = cls; | ||
632 | |||
633 | ts->channel = | ||
634 | NULL; /* we must not call GNUNET_CADET_channel_destroy() anymore */ | ||
635 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
636 | "CADET notified us about death of channel to `%s'\n", | ||
637 | print_channel_destination (&ts->destination)); | ||
638 | free_channel_state (ts); | ||
639 | } | ||
640 | |||
641 | |||
642 | /** | ||
643 | * Synthesize a plausible ICMP payload for an ICMP error | ||
644 | * response on the given channel. | ||
645 | * | ||
646 | * @param ts channel information | ||
647 | * @param ipp IPv4 header to fill in (ICMP payload) | ||
648 | * @param udp "UDP" header to fill in (ICMP payload); might actually | ||
649 | * also be the first 8 bytes of the TCP header | ||
650 | */ | ||
651 | static void | ||
652 | make_up_icmpv4_payload (struct ChannelState *ts, | ||
653 | struct GNUNET_TUN_IPv4Header *ipp, | ||
654 | struct GNUNET_TUN_UdpHeader *udp) | ||
655 | { | ||
656 | GNUNET_TUN_initialize_ipv4_header (ipp, | ||
657 | ts->protocol, | ||
658 | sizeof(struct GNUNET_TUN_TcpHeader), | ||
659 | &ts->source_ip.v4, | ||
660 | &ts->destination_ip.v4); | ||
661 | udp->source_port = htons (ts->source_port); | ||
662 | udp->destination_port = htons (ts->destination_port); | ||
663 | udp->len = htons (0); | ||
664 | udp->crc = htons (0); | ||
665 | } | ||
666 | |||
667 | |||
668 | /** | ||
669 | * Synthesize a plausible ICMP payload for an ICMP error | ||
670 | * response on the given channel. | ||
671 | * | ||
672 | * @param ts channel information | ||
673 | * @param ipp IPv6 header to fill in (ICMP payload) | ||
674 | * @param udp "UDP" header to fill in (ICMP payload); might actually | ||
675 | * also be the first 8 bytes of the TCP header | ||
676 | */ | ||
677 | static void | ||
678 | make_up_icmpv6_payload (struct ChannelState *ts, | ||
679 | struct GNUNET_TUN_IPv6Header *ipp, | ||
680 | struct GNUNET_TUN_UdpHeader *udp) | ||
681 | { | ||
682 | GNUNET_TUN_initialize_ipv6_header (ipp, | ||
683 | ts->protocol, | ||
684 | sizeof(struct GNUNET_TUN_TcpHeader), | ||
685 | &ts->source_ip.v6, | ||
686 | &ts->destination_ip.v6); | ||
687 | udp->source_port = htons (ts->source_port); | ||
688 | udp->destination_port = htons (ts->destination_port); | ||
689 | udp->len = htons (0); | ||
690 | udp->crc = htons (0); | ||
691 | } | ||
692 | |||
693 | |||
694 | /** | ||
695 | * We got an ICMP packet back from the CADET channel. Check it is OK. | ||
696 | * | ||
697 | * @param cls our `struct ChannelState *` | ||
698 | * @param i2v the actual message | ||
699 | * @return #GNUNET_OK to keep the connection open, | ||
700 | * #GNUNET_SYSERR to close it (signal serious error) | ||
701 | */ | ||
702 | static int | ||
703 | check_icmp_back (void *cls, const struct GNUNET_EXIT_IcmpToVPNMessage *i2v) | ||
704 | { | ||
705 | struct ChannelState *ts = cls; | ||
706 | |||
707 | if (NULL == ts->heap_node) | ||
708 | { | ||
709 | GNUNET_break_op (0); | ||
710 | return GNUNET_SYSERR; | ||
711 | } | ||
712 | if (AF_UNSPEC == ts->af) | ||
713 | { | ||
714 | GNUNET_break_op (0); | ||
715 | return GNUNET_SYSERR; | ||
716 | } | ||
717 | return GNUNET_OK; | ||
718 | } | ||
719 | |||
720 | |||
721 | /** | ||
722 | * We got an ICMP packet back from the CADET channel. Pass it on to the | ||
723 | * local virtual interface via the helper. | ||
724 | * | ||
725 | * @param cls our `struct ChannelState *` | ||
726 | * @param i2v the actual message | ||
727 | */ | ||
728 | static void | ||
729 | handle_icmp_back (void *cls, const struct GNUNET_EXIT_IcmpToVPNMessage *i2v) | ||
730 | { | ||
731 | struct ChannelState *ts = cls; | ||
732 | size_t mlen; | ||
733 | |||
734 | GNUNET_STATISTICS_update (stats, | ||
735 | gettext_noop ("# ICMP packets received from cadet"), | ||
736 | 1, | ||
737 | GNUNET_NO); | ||
738 | mlen = | ||
739 | ntohs (i2v->header.size) - sizeof(struct GNUNET_EXIT_IcmpToVPNMessage); | ||
740 | { | ||
741 | char sbuf[INET6_ADDRSTRLEN]; | ||
742 | char dbuf[INET6_ADDRSTRLEN]; | ||
743 | |||
744 | GNUNET_log ( | ||
745 | GNUNET_ERROR_TYPE_DEBUG, | ||
746 | "Received ICMP packet from cadet, sending %u bytes from %s -> %s via TUN\n", | ||
747 | (unsigned int) mlen, | ||
748 | inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof(sbuf)), | ||
749 | inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof(dbuf))); | ||
750 | } | ||
751 | switch (ts->af) | ||
752 | { | ||
753 | case AF_INET: { | ||
754 | size_t size = sizeof(struct GNUNET_TUN_IPv4Header) | ||
755 | + sizeof(struct GNUNET_TUN_IcmpHeader) | ||
756 | + sizeof(struct GNUNET_MessageHeader) | ||
757 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
758 | { | ||
759 | /* reserve some extra space in case we have an ICMP type here where | ||
760 | we will need to make up the payload ourselves */ | ||
761 | char buf[size + sizeof(struct GNUNET_TUN_IPv4Header) + 8] GNUNET_ALIGN; | ||
762 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
763 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
764 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
765 | struct GNUNET_TUN_IPv4Header *ipv4 = | ||
766 | (struct GNUNET_TUN_IPv4Header *) &tun[1]; | ||
767 | struct GNUNET_TUN_IcmpHeader *icmp = | ||
768 | (struct GNUNET_TUN_IcmpHeader *) &ipv4[1]; | ||
769 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
770 | tun->flags = htons (0); | ||
771 | tun->proto = htons (ETH_P_IPV4); | ||
772 | GNUNET_TUN_initialize_ipv4_header (ipv4, | ||
773 | IPPROTO_ICMP, | ||
774 | sizeof(struct GNUNET_TUN_IcmpHeader) | ||
775 | + mlen, | ||
776 | &ts->destination_ip.v4, | ||
777 | &ts->source_ip.v4); | ||
778 | *icmp = i2v->icmp_header; | ||
779 | GNUNET_memcpy (&icmp[1], &i2v[1], mlen); | ||
780 | /* For some ICMP types, we need to adjust (make up) the payload here. | ||
781 | Also, depending on the AF used on the other side, we have to | ||
782 | do ICMP PT (translate ICMP types) */ | ||
783 | switch (ntohl (i2v->af)) | ||
784 | { | ||
785 | case AF_INET: | ||
786 | switch (icmp->type) | ||
787 | { | ||
788 | case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: | ||
789 | case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: | ||
790 | break; | ||
791 | |||
792 | case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: | ||
793 | case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: | ||
794 | case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: { | ||
795 | struct GNUNET_TUN_IPv4Header *ipp = | ||
796 | (struct GNUNET_TUN_IPv4Header *) &icmp[1]; | ||
797 | struct GNUNET_TUN_UdpHeader *udp = | ||
798 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
799 | |||
800 | if (mlen != 0) | ||
801 | { | ||
802 | /* sender did not strip ICMP payload? */ | ||
803 | GNUNET_break_op (0); | ||
804 | return; | ||
805 | } | ||
806 | size += sizeof(struct GNUNET_TUN_IPv4Header) + 8; | ||
807 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
808 | make_up_icmpv4_payload (ts, ipp, udp); | ||
809 | } | ||
810 | break; | ||
811 | |||
812 | default: | ||
813 | GNUNET_break_op (0); | ||
814 | GNUNET_STATISTICS_update ( | ||
815 | stats, | ||
816 | gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), | ||
817 | 1, | ||
818 | GNUNET_NO); | ||
819 | return; | ||
820 | } | ||
821 | /* end AF_INET */ | ||
822 | break; | ||
823 | |||
824 | case AF_INET6: | ||
825 | /* ICMP PT 6-to-4 and possibly making up payloads */ | ||
826 | switch (icmp->type) | ||
827 | { | ||
828 | case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: | ||
829 | icmp->type = GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE; | ||
830 | { | ||
831 | struct GNUNET_TUN_IPv4Header *ipp = | ||
832 | (struct GNUNET_TUN_IPv4Header *) &icmp[1]; | ||
833 | struct GNUNET_TUN_UdpHeader *udp = | ||
834 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
835 | |||
836 | if (mlen != 0) | ||
837 | { | ||
838 | /* sender did not strip ICMP payload? */ | ||
839 | GNUNET_break_op (0); | ||
840 | return; | ||
841 | } | ||
842 | size += sizeof(struct GNUNET_TUN_IPv4Header) + 8; | ||
843 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
844 | make_up_icmpv4_payload (ts, ipp, udp); | ||
845 | } | ||
846 | break; | ||
847 | |||
848 | case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: | ||
849 | icmp->type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED; | ||
850 | { | ||
851 | struct GNUNET_TUN_IPv4Header *ipp = | ||
852 | (struct GNUNET_TUN_IPv4Header *) &icmp[1]; | ||
853 | struct GNUNET_TUN_UdpHeader *udp = | ||
854 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
855 | |||
856 | if (mlen != 0) | ||
857 | { | ||
858 | /* sender did not strip ICMP payload? */ | ||
859 | GNUNET_break_op (0); | ||
860 | return; | ||
861 | } | ||
862 | size += sizeof(struct GNUNET_TUN_IPv4Header) + 8; | ||
863 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
864 | make_up_icmpv4_payload (ts, ipp, udp); | ||
865 | } | ||
866 | break; | ||
867 | |||
868 | case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: | ||
869 | case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: | ||
870 | GNUNET_STATISTICS_update ( | ||
871 | stats, | ||
872 | gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"), | ||
873 | 1, | ||
874 | GNUNET_NO); | ||
875 | return; | ||
876 | |||
877 | case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: | ||
878 | icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST; | ||
879 | break; | ||
880 | |||
881 | case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY: | ||
882 | icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY; | ||
883 | break; | ||
884 | |||
885 | default: | ||
886 | GNUNET_break_op (0); | ||
887 | GNUNET_STATISTICS_update ( | ||
888 | stats, | ||
889 | gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), | ||
890 | 1, | ||
891 | GNUNET_NO); | ||
892 | return; | ||
893 | } | ||
894 | /* end AF_INET6 */ | ||
895 | break; | ||
896 | |||
897 | default: | ||
898 | GNUNET_break_op (0); | ||
899 | return; | ||
900 | } | ||
901 | msg->size = htons (size); | ||
902 | GNUNET_TUN_calculate_icmp_checksum (icmp, &i2v[1], mlen); | ||
903 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
904 | } | ||
905 | } | ||
906 | break; | ||
907 | |||
908 | case AF_INET6: { | ||
909 | size_t size = sizeof(struct GNUNET_TUN_IPv6Header) | ||
910 | + sizeof(struct GNUNET_TUN_IcmpHeader) | ||
911 | + sizeof(struct GNUNET_MessageHeader) | ||
912 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
913 | { | ||
914 | char buf[size + sizeof(struct GNUNET_TUN_IPv6Header) + 8] GNUNET_ALIGN; | ||
915 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
916 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
917 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
918 | struct GNUNET_TUN_IPv6Header *ipv6 = | ||
919 | (struct GNUNET_TUN_IPv6Header *) &tun[1]; | ||
920 | struct GNUNET_TUN_IcmpHeader *icmp = | ||
921 | (struct GNUNET_TUN_IcmpHeader *) &ipv6[1]; | ||
922 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
923 | tun->flags = htons (0); | ||
924 | tun->proto = htons (ETH_P_IPV6); | ||
925 | GNUNET_TUN_initialize_ipv6_header (ipv6, | ||
926 | IPPROTO_ICMPV6, | ||
927 | sizeof(struct GNUNET_TUN_IcmpHeader) | ||
928 | + mlen, | ||
929 | &ts->destination_ip.v6, | ||
930 | &ts->source_ip.v6); | ||
931 | *icmp = i2v->icmp_header; | ||
932 | GNUNET_memcpy (&icmp[1], &i2v[1], mlen); | ||
933 | |||
934 | /* For some ICMP types, we need to adjust (make up) the payload here. | ||
935 | Also, depending on the AF used on the other side, we have to | ||
936 | do ICMP PT (translate ICMP types) */ | ||
937 | switch (ntohl (i2v->af)) | ||
938 | { | ||
939 | case AF_INET: | ||
940 | /* ICMP PT 4-to-6 and possibly making up payloads */ | ||
941 | switch (icmp->type) | ||
942 | { | ||
943 | case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: | ||
944 | icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY; | ||
945 | break; | ||
946 | |||
947 | case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: | ||
948 | icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST; | ||
949 | break; | ||
950 | |||
951 | case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: | ||
952 | icmp->type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE; | ||
953 | { | ||
954 | struct GNUNET_TUN_IPv6Header *ipp = | ||
955 | (struct GNUNET_TUN_IPv6Header *) &icmp[1]; | ||
956 | struct GNUNET_TUN_UdpHeader *udp = | ||
957 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
958 | |||
959 | if (mlen != 0) | ||
960 | { | ||
961 | /* sender did not strip ICMP payload? */ | ||
962 | GNUNET_break_op (0); | ||
963 | return; | ||
964 | } | ||
965 | size += sizeof(struct GNUNET_TUN_IPv6Header) + 8; | ||
966 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
967 | make_up_icmpv6_payload (ts, ipp, udp); | ||
968 | } | ||
969 | break; | ||
970 | |||
971 | case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: | ||
972 | icmp->type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED; | ||
973 | { | ||
974 | struct GNUNET_TUN_IPv6Header *ipp = | ||
975 | (struct GNUNET_TUN_IPv6Header *) &icmp[1]; | ||
976 | struct GNUNET_TUN_UdpHeader *udp = | ||
977 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
978 | |||
979 | if (mlen != 0) | ||
980 | { | ||
981 | /* sender did not strip ICMP payload? */ | ||
982 | GNUNET_break_op (0); | ||
983 | return; | ||
984 | } | ||
985 | size += sizeof(struct GNUNET_TUN_IPv6Header) + 8; | ||
986 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
987 | make_up_icmpv6_payload (ts, ipp, udp); | ||
988 | } | ||
989 | break; | ||
990 | |||
991 | case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: | ||
992 | GNUNET_STATISTICS_update ( | ||
993 | stats, | ||
994 | gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"), | ||
995 | 1, | ||
996 | GNUNET_NO); | ||
997 | return; | ||
998 | |||
999 | default: | ||
1000 | GNUNET_break_op (0); | ||
1001 | GNUNET_STATISTICS_update ( | ||
1002 | stats, | ||
1003 | gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), | ||
1004 | 1, | ||
1005 | GNUNET_NO); | ||
1006 | return; | ||
1007 | } | ||
1008 | /* end AF_INET */ | ||
1009 | break; | ||
1010 | |||
1011 | case AF_INET6: | ||
1012 | switch (icmp->type) | ||
1013 | { | ||
1014 | case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: | ||
1015 | case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: | ||
1016 | case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: | ||
1017 | case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: { | ||
1018 | struct GNUNET_TUN_IPv6Header *ipp = | ||
1019 | (struct GNUNET_TUN_IPv6Header *) &icmp[1]; | ||
1020 | struct GNUNET_TUN_UdpHeader *udp = | ||
1021 | (struct GNUNET_TUN_UdpHeader *) &ipp[1]; | ||
1022 | |||
1023 | if (mlen != 0) | ||
1024 | { | ||
1025 | /* sender did not strip ICMP payload? */ | ||
1026 | GNUNET_break_op (0); | ||
1027 | return; | ||
1028 | } | ||
1029 | size += sizeof(struct GNUNET_TUN_IPv6Header) + 8; | ||
1030 | GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader)); | ||
1031 | make_up_icmpv6_payload (ts, ipp, udp); | ||
1032 | } | ||
1033 | break; | ||
1034 | |||
1035 | case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: | ||
1036 | break; | ||
1037 | |||
1038 | default: | ||
1039 | GNUNET_break_op (0); | ||
1040 | GNUNET_STATISTICS_update ( | ||
1041 | stats, | ||
1042 | gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), | ||
1043 | 1, | ||
1044 | GNUNET_NO); | ||
1045 | return; | ||
1046 | } | ||
1047 | /* end AF_INET6 */ | ||
1048 | break; | ||
1049 | |||
1050 | default: | ||
1051 | GNUNET_break_op (0); | ||
1052 | return; | ||
1053 | } | ||
1054 | msg->size = htons (size); | ||
1055 | GNUNET_TUN_calculate_icmp_checksum (icmp, &i2v[1], mlen); | ||
1056 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
1057 | } | ||
1058 | } | ||
1059 | break; | ||
1060 | |||
1061 | default: | ||
1062 | GNUNET_assert (0); | ||
1063 | } | ||
1064 | GNUNET_CONTAINER_heap_update_cost (ts->heap_node, | ||
1065 | GNUNET_TIME_absolute_get ().abs_value_us); | ||
1066 | GNUNET_CADET_receive_done (ts->channel); | ||
1067 | } | ||
1068 | |||
1069 | |||
1070 | /** | ||
1071 | * We got a UDP packet back from the CADET channel. Check that it is OK. | ||
1072 | * | ||
1073 | * @param cls our `struct ChannelState *` | ||
1074 | * @param reply the actual message | ||
1075 | * @return #GNUNET_OK to keep the connection open, | ||
1076 | * #GNUNET_SYSERR to close it (signal serious error) | ||
1077 | */ | ||
1078 | static int | ||
1079 | check_udp_back (void *cls, const struct GNUNET_EXIT_UdpReplyMessage *reply) | ||
1080 | { | ||
1081 | struct ChannelState *ts = cls; | ||
1082 | |||
1083 | if (NULL == ts->heap_node) | ||
1084 | { | ||
1085 | GNUNET_break_op (0); | ||
1086 | return GNUNET_SYSERR; | ||
1087 | } | ||
1088 | if (AF_UNSPEC == ts->af) | ||
1089 | { | ||
1090 | GNUNET_break_op (0); | ||
1091 | return GNUNET_SYSERR; | ||
1092 | } | ||
1093 | return GNUNET_OK; | ||
1094 | } | ||
1095 | |||
1096 | |||
1097 | /** | ||
1098 | * We got a UDP packet back from the CADET channel. Pass it on to the | ||
1099 | * local virtual interface via the helper. | ||
1100 | * | ||
1101 | * @param cls our `struct ChannelState *` | ||
1102 | * @param reply the actual message | ||
1103 | */ | ||
1104 | static void | ||
1105 | handle_udp_back (void *cls, const struct GNUNET_EXIT_UdpReplyMessage *reply) | ||
1106 | { | ||
1107 | struct ChannelState *ts = cls; | ||
1108 | size_t mlen; | ||
1109 | |||
1110 | GNUNET_STATISTICS_update (stats, | ||
1111 | gettext_noop ("# UDP packets received from cadet"), | ||
1112 | 1, | ||
1113 | GNUNET_NO); | ||
1114 | mlen = | ||
1115 | ntohs (reply->header.size) - sizeof(struct GNUNET_EXIT_UdpReplyMessage); | ||
1116 | { | ||
1117 | char sbuf[INET6_ADDRSTRLEN]; | ||
1118 | char dbuf[INET6_ADDRSTRLEN]; | ||
1119 | |||
1120 | GNUNET_log ( | ||
1121 | GNUNET_ERROR_TYPE_DEBUG, | ||
1122 | "Received UDP reply from cadet, sending %u bytes from [%s]:%u -> [%s]:%u via TUN\n", | ||
1123 | (unsigned int) mlen, | ||
1124 | inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof(sbuf)), | ||
1125 | ts->destination_port, | ||
1126 | inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof(dbuf)), | ||
1127 | ts->source_port); | ||
1128 | } | ||
1129 | switch (ts->af) | ||
1130 | { | ||
1131 | case AF_INET: { | ||
1132 | size_t size = sizeof(struct GNUNET_TUN_IPv4Header) | ||
1133 | + sizeof(struct GNUNET_TUN_UdpHeader) | ||
1134 | + sizeof(struct GNUNET_MessageHeader) | ||
1135 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
1136 | { | ||
1137 | char buf[size] GNUNET_ALIGN; | ||
1138 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
1139 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
1140 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
1141 | struct GNUNET_TUN_IPv4Header *ipv4 = | ||
1142 | (struct GNUNET_TUN_IPv4Header *) &tun[1]; | ||
1143 | struct GNUNET_TUN_UdpHeader *udp = | ||
1144 | (struct GNUNET_TUN_UdpHeader *) &ipv4[1]; | ||
1145 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1146 | msg->size = htons (size); | ||
1147 | tun->flags = htons (0); | ||
1148 | tun->proto = htons (ETH_P_IPV4); | ||
1149 | GNUNET_TUN_initialize_ipv4_header (ipv4, | ||
1150 | IPPROTO_UDP, | ||
1151 | sizeof(struct GNUNET_TUN_UdpHeader) | ||
1152 | + mlen, | ||
1153 | &ts->destination_ip.v4, | ||
1154 | &ts->source_ip.v4); | ||
1155 | if (0 == ntohs (reply->source_port)) | ||
1156 | udp->source_port = htons (ts->destination_port); | ||
1157 | else | ||
1158 | udp->source_port = reply->source_port; | ||
1159 | if (0 == ntohs (reply->destination_port)) | ||
1160 | udp->destination_port = htons (ts->source_port); | ||
1161 | else | ||
1162 | udp->destination_port = reply->destination_port; | ||
1163 | udp->len = htons (mlen + sizeof(struct GNUNET_TUN_UdpHeader)); | ||
1164 | GNUNET_TUN_calculate_udp4_checksum (ipv4, udp, &reply[1], mlen); | ||
1165 | GNUNET_memcpy (&udp[1], &reply[1], mlen); | ||
1166 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
1167 | } | ||
1168 | } | ||
1169 | break; | ||
1170 | |||
1171 | case AF_INET6: { | ||
1172 | size_t size = sizeof(struct GNUNET_TUN_IPv6Header) | ||
1173 | + sizeof(struct GNUNET_TUN_UdpHeader) | ||
1174 | + sizeof(struct GNUNET_MessageHeader) | ||
1175 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
1176 | { | ||
1177 | char buf[size] GNUNET_ALIGN; | ||
1178 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
1179 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
1180 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
1181 | struct GNUNET_TUN_IPv6Header *ipv6 = | ||
1182 | (struct GNUNET_TUN_IPv6Header *) &tun[1]; | ||
1183 | struct GNUNET_TUN_UdpHeader *udp = | ||
1184 | (struct GNUNET_TUN_UdpHeader *) &ipv6[1]; | ||
1185 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1186 | msg->size = htons (size); | ||
1187 | tun->flags = htons (0); | ||
1188 | tun->proto = htons (ETH_P_IPV6); | ||
1189 | GNUNET_TUN_initialize_ipv6_header (ipv6, | ||
1190 | IPPROTO_UDP, | ||
1191 | sizeof(struct GNUNET_TUN_UdpHeader) | ||
1192 | + mlen, | ||
1193 | &ts->destination_ip.v6, | ||
1194 | &ts->source_ip.v6); | ||
1195 | if (0 == ntohs (reply->source_port)) | ||
1196 | udp->source_port = htons (ts->destination_port); | ||
1197 | else | ||
1198 | udp->source_port = reply->source_port; | ||
1199 | if (0 == ntohs (reply->destination_port)) | ||
1200 | udp->destination_port = htons (ts->source_port); | ||
1201 | else | ||
1202 | udp->destination_port = reply->destination_port; | ||
1203 | udp->len = htons (mlen + sizeof(struct GNUNET_TUN_UdpHeader)); | ||
1204 | GNUNET_TUN_calculate_udp6_checksum (ipv6, udp, &reply[1], mlen); | ||
1205 | GNUNET_memcpy (&udp[1], &reply[1], mlen); | ||
1206 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
1207 | } | ||
1208 | } | ||
1209 | break; | ||
1210 | |||
1211 | default: | ||
1212 | GNUNET_assert (0); | ||
1213 | } | ||
1214 | GNUNET_CONTAINER_heap_update_cost (ts->heap_node, | ||
1215 | GNUNET_TIME_absolute_get ().abs_value_us); | ||
1216 | GNUNET_CADET_receive_done (ts->channel); | ||
1217 | } | ||
1218 | |||
1219 | |||
1220 | /** | ||
1221 | * We got a TCP packet back from the CADET channel. Check it is OK. | ||
1222 | * | ||
1223 | * @param cls our `struct ChannelState *` | ||
1224 | * @param data the actual message | ||
1225 | * @return #GNUNET_OK to keep the connection open, | ||
1226 | * #GNUNET_SYSERR to close it (signal serious error) | ||
1227 | */ | ||
1228 | static int | ||
1229 | check_tcp_back (void *cls, const struct GNUNET_EXIT_TcpDataMessage *data) | ||
1230 | { | ||
1231 | struct ChannelState *ts = cls; | ||
1232 | |||
1233 | if (NULL == ts->heap_node) | ||
1234 | { | ||
1235 | GNUNET_break_op (0); | ||
1236 | return GNUNET_SYSERR; | ||
1237 | } | ||
1238 | if (data->tcp_header.off * 4 < sizeof(struct GNUNET_TUN_TcpHeader)) | ||
1239 | { | ||
1240 | GNUNET_break_op (0); | ||
1241 | return GNUNET_SYSERR; | ||
1242 | } | ||
1243 | return GNUNET_OK; | ||
1244 | } | ||
1245 | |||
1246 | |||
1247 | /** | ||
1248 | * We got a TCP packet back from the CADET channel. Pass it on to the | ||
1249 | * local virtual interface via the helper. | ||
1250 | * | ||
1251 | * @param cls our `struct ChannelState *` | ||
1252 | * @param data the actual message | ||
1253 | */ | ||
1254 | static void | ||
1255 | handle_tcp_back (void *cls, const struct GNUNET_EXIT_TcpDataMessage *data) | ||
1256 | { | ||
1257 | struct ChannelState *ts = cls; | ||
1258 | size_t mlen; | ||
1259 | |||
1260 | GNUNET_STATISTICS_update (stats, | ||
1261 | gettext_noop ("# TCP packets received from cadet"), | ||
1262 | 1, | ||
1263 | GNUNET_NO); | ||
1264 | mlen = ntohs (data->header.size) - sizeof(struct GNUNET_EXIT_TcpDataMessage); | ||
1265 | { | ||
1266 | char sbuf[INET6_ADDRSTRLEN]; | ||
1267 | char dbuf[INET6_ADDRSTRLEN]; | ||
1268 | |||
1269 | GNUNET_log ( | ||
1270 | GNUNET_ERROR_TYPE_DEBUG, | ||
1271 | "Received TCP reply from cadet, sending %u bytes from [%s]:%u -> [%s]:%u via TUN\n", | ||
1272 | (unsigned int) mlen, | ||
1273 | inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof(sbuf)), | ||
1274 | ts->destination_port, | ||
1275 | inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof(dbuf)), | ||
1276 | ts->source_port); | ||
1277 | } | ||
1278 | switch (ts->af) | ||
1279 | { | ||
1280 | case AF_INET: { | ||
1281 | size_t size = sizeof(struct GNUNET_TUN_IPv4Header) | ||
1282 | + sizeof(struct GNUNET_TUN_TcpHeader) | ||
1283 | + sizeof(struct GNUNET_MessageHeader) | ||
1284 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
1285 | { | ||
1286 | char buf[size] GNUNET_ALIGN; | ||
1287 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
1288 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
1289 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
1290 | struct GNUNET_TUN_IPv4Header *ipv4 = | ||
1291 | (struct GNUNET_TUN_IPv4Header *) &tun[1]; | ||
1292 | struct GNUNET_TUN_TcpHeader *tcp = | ||
1293 | (struct GNUNET_TUN_TcpHeader *) &ipv4[1]; | ||
1294 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1295 | msg->size = htons (size); | ||
1296 | tun->flags = htons (0); | ||
1297 | tun->proto = htons (ETH_P_IPV4); | ||
1298 | GNUNET_TUN_initialize_ipv4_header (ipv4, | ||
1299 | IPPROTO_TCP, | ||
1300 | sizeof(struct GNUNET_TUN_TcpHeader) | ||
1301 | + mlen, | ||
1302 | &ts->destination_ip.v4, | ||
1303 | &ts->source_ip.v4); | ||
1304 | *tcp = data->tcp_header; | ||
1305 | tcp->source_port = htons (ts->destination_port); | ||
1306 | tcp->destination_port = htons (ts->source_port); | ||
1307 | GNUNET_TUN_calculate_tcp4_checksum (ipv4, tcp, &data[1], mlen); | ||
1308 | GNUNET_memcpy (&tcp[1], &data[1], mlen); | ||
1309 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
1310 | } | ||
1311 | } | ||
1312 | break; | ||
1313 | |||
1314 | case AF_INET6: { | ||
1315 | size_t size = sizeof(struct GNUNET_TUN_IPv6Header) | ||
1316 | + sizeof(struct GNUNET_TUN_TcpHeader) | ||
1317 | + sizeof(struct GNUNET_MessageHeader) | ||
1318 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader) + mlen; | ||
1319 | { | ||
1320 | char buf[size] GNUNET_ALIGN; | ||
1321 | struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf; | ||
1322 | struct GNUNET_TUN_Layer2PacketHeader *tun = | ||
1323 | (struct GNUNET_TUN_Layer2PacketHeader *) &msg[1]; | ||
1324 | struct GNUNET_TUN_IPv6Header *ipv6 = | ||
1325 | (struct GNUNET_TUN_IPv6Header *) &tun[1]; | ||
1326 | struct GNUNET_TUN_TcpHeader *tcp = | ||
1327 | (struct GNUNET_TUN_TcpHeader *) &ipv6[1]; | ||
1328 | msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1329 | msg->size = htons (size); | ||
1330 | tun->flags = htons (0); | ||
1331 | tun->proto = htons (ETH_P_IPV6); | ||
1332 | GNUNET_TUN_initialize_ipv6_header (ipv6, | ||
1333 | IPPROTO_TCP, | ||
1334 | sizeof(struct GNUNET_TUN_TcpHeader) | ||
1335 | + mlen, | ||
1336 | &ts->destination_ip.v6, | ||
1337 | &ts->source_ip.v6); | ||
1338 | *tcp = data->tcp_header; | ||
1339 | tcp->source_port = htons (ts->destination_port); | ||
1340 | tcp->destination_port = htons (ts->source_port); | ||
1341 | GNUNET_TUN_calculate_tcp6_checksum (ipv6, tcp, &data[1], mlen); | ||
1342 | GNUNET_memcpy (&tcp[1], &data[1], mlen); | ||
1343 | (void) GNUNET_HELPER_send (helper_handle, msg, GNUNET_YES, NULL, NULL); | ||
1344 | } | ||
1345 | } | ||
1346 | break; | ||
1347 | } | ||
1348 | GNUNET_CONTAINER_heap_update_cost (ts->heap_node, | ||
1349 | GNUNET_TIME_absolute_get ().abs_value_us); | ||
1350 | GNUNET_CADET_receive_done (ts->channel); | ||
1351 | } | ||
1352 | |||
1353 | |||
1354 | /** | ||
1355 | * Create a channel for @a ts to @a target at @a port | ||
1356 | * | ||
1357 | * @param ts channel state to create the channel for | ||
1358 | * @param target peer to connect to | ||
1359 | * @param port destination port | ||
1360 | * @return the channel handle | ||
1361 | */ | ||
1362 | static struct GNUNET_CADET_Channel * | ||
1363 | create_channel (struct ChannelState *ts, | ||
1364 | const struct GNUNET_PeerIdentity *target, | ||
1365 | const struct GNUNET_HashCode *port) | ||
1366 | { | ||
1367 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = | ||
1368 | { GNUNET_MQ_hd_var_size (udp_back, | ||
1369 | GNUNET_MESSAGE_TYPE_VPN_UDP_REPLY, | ||
1370 | struct GNUNET_EXIT_UdpReplyMessage, | ||
1371 | ts), | ||
1372 | GNUNET_MQ_hd_var_size (tcp_back, | ||
1373 | GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_VPN, | ||
1374 | struct GNUNET_EXIT_TcpDataMessage, | ||
1375 | ts), | ||
1376 | GNUNET_MQ_hd_var_size (icmp_back, | ||
1377 | GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_VPN, | ||
1378 | struct GNUNET_EXIT_IcmpToVPNMessage, | ||
1379 | ts), | ||
1380 | GNUNET_MQ_handler_end () }; | ||
1381 | |||
1382 | return GNUNET_CADET_channel_create (cadet_handle, | ||
1383 | ts, | ||
1384 | target, | ||
1385 | port, | ||
1386 | NULL, | ||
1387 | &channel_cleaner, | ||
1388 | cadet_handlers); | ||
1389 | } | ||
1390 | |||
1391 | |||
1392 | /** | ||
1393 | * Regex has found a potential exit peer for us; consider using it. | ||
1394 | * | ||
1395 | * @param cls the `struct ChannelState` | ||
1396 | * @param id Peer providing a regex that matches the string. | ||
1397 | * @param get_path Path of the get request. | ||
1398 | * @param get_path_length Length of @a get_path. | ||
1399 | * @param put_path Path of the put request. | ||
1400 | * @param put_path_length Length of the @a put_path. | ||
1401 | */ | ||
1402 | static void | ||
1403 | handle_regex_result (void *cls, | ||
1404 | const struct GNUNET_PeerIdentity *id, | ||
1405 | const struct GNUNET_PeerIdentity *get_path, | ||
1406 | unsigned int get_path_length, | ||
1407 | const struct GNUNET_PeerIdentity *put_path, | ||
1408 | unsigned int put_path_length) | ||
1409 | { | ||
1410 | struct ChannelState *ts = cls; | ||
1411 | struct GNUNET_HashCode port; | ||
1412 | |||
1413 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1414 | "Exit %s found for destination %s!\n", | ||
1415 | GNUNET_i2s (id), | ||
1416 | print_channel_destination (&ts->destination)); | ||
1417 | GNUNET_REGEX_search_cancel (ts->search); | ||
1418 | ts->search = NULL; | ||
1419 | switch (ts->af) | ||
1420 | { | ||
1421 | case AF_INET: | ||
1422 | /* these must match the strings used in gnunet-daemon-exit */ | ||
1423 | GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_IPV4_GATEWAY, | ||
1424 | strlen (GNUNET_APPLICATION_PORT_IPV4_GATEWAY), | ||
1425 | &port); | ||
1426 | break; | ||
1427 | |||
1428 | case AF_INET6: | ||
1429 | /* these must match the strings used in gnunet-daemon-exit */ | ||
1430 | GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_IPV6_GATEWAY, | ||
1431 | strlen (GNUNET_APPLICATION_PORT_IPV6_GATEWAY), | ||
1432 | &port); | ||
1433 | break; | ||
1434 | |||
1435 | default: | ||
1436 | GNUNET_break (0); | ||
1437 | return; | ||
1438 | } | ||
1439 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1440 | "Creating tunnel to %s for destination %s!\n", | ||
1441 | GNUNET_i2s (id), | ||
1442 | print_channel_destination (&ts->destination)); | ||
1443 | ts->channel = create_channel (ts, id, &port); | ||
1444 | } | ||
1445 | |||
1446 | |||
1447 | /** | ||
1448 | * Initialize the given destination entry's cadet channel. | ||
1449 | * | ||
1450 | * @param dt destination channel for which we need to setup a channel | ||
1451 | * @param client_af address family of the address returned to the client | ||
1452 | * @return channel state of the channel that was created | ||
1453 | */ | ||
1454 | static struct ChannelState * | ||
1455 | create_channel_to_destination (struct DestinationChannel *dt, int client_af) | ||
1456 | { | ||
1457 | struct ChannelState *ts; | ||
1458 | |||
1459 | GNUNET_STATISTICS_update (stats, | ||
1460 | gettext_noop ("# Cadet channels created"), | ||
1461 | 1, | ||
1462 | GNUNET_NO); | ||
1463 | ts = GNUNET_new (struct ChannelState); | ||
1464 | ts->af = client_af; | ||
1465 | ts->destination = *dt->destination; | ||
1466 | ts->destination.heap_node = NULL; /* copy is NOT in destination heap */ | ||
1467 | ts->destination_port = dt->destination_port; | ||
1468 | if (dt->destination->is_service) | ||
1469 | { | ||
1470 | struct GNUNET_HashCode cadet_port; | ||
1471 | |||
1472 | GNUNET_TUN_compute_service_cadet_port (&ts->destination.details | ||
1473 | .service_destination | ||
1474 | .service_descriptor, | ||
1475 | ts->destination_port, | ||
1476 | &cadet_port); | ||
1477 | ts->channel = | ||
1478 | create_channel (ts, | ||
1479 | &dt->destination->details.service_destination.target, | ||
1480 | &cadet_port); | ||
1481 | |||
1482 | if (NULL == ts->channel) | ||
1483 | { | ||
1484 | GNUNET_break (0); | ||
1485 | GNUNET_free (ts); | ||
1486 | return NULL; | ||
1487 | } | ||
1488 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1489 | "Creating channel to peer %s offering service %s on port %u\n", | ||
1490 | GNUNET_i2s ( | ||
1491 | &dt->destination->details.service_destination.target), | ||
1492 | GNUNET_h2s (&ts->destination.details.service_destination | ||
1493 | .service_descriptor), | ||
1494 | (unsigned int) ts->destination_port); | ||
1495 | } | ||
1496 | else | ||
1497 | { | ||
1498 | char *policy; | ||
1499 | |||
1500 | switch (dt->destination->details.exit_destination.af) | ||
1501 | { | ||
1502 | case AF_INET: { | ||
1503 | char address[GNUNET_TUN_IPV4_REGEXLEN]; | ||
1504 | |||
1505 | GNUNET_TUN_ipv4toregexsearch (&dt->destination->details.exit_destination | ||
1506 | .ip.v4, | ||
1507 | dt->destination_port, | ||
1508 | address); | ||
1509 | GNUNET_asprintf (&policy, | ||
1510 | "%s%s", | ||
1511 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
1512 | address); | ||
1513 | break; | ||
1514 | } | ||
1515 | |||
1516 | case AF_INET6: { | ||
1517 | char address[GNUNET_TUN_IPV6_REGEXLEN]; | ||
1518 | |||
1519 | GNUNET_TUN_ipv6toregexsearch (&dt->destination->details.exit_destination | ||
1520 | .ip.v6, | ||
1521 | dt->destination_port, | ||
1522 | address); | ||
1523 | GNUNET_asprintf (&policy, | ||
1524 | "%s%s", | ||
1525 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
1526 | address); | ||
1527 | break; | ||
1528 | } | ||
1529 | |||
1530 | default: | ||
1531 | GNUNET_assert (0); | ||
1532 | break; | ||
1533 | } | ||
1534 | |||
1535 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1536 | "Requesting connect by string: %s\n", | ||
1537 | policy); | ||
1538 | ts->search = GNUNET_REGEX_search (cfg, policy, &handle_regex_result, ts); | ||
1539 | GNUNET_free (policy); | ||
1540 | } | ||
1541 | return ts; | ||
1542 | } | ||
1543 | |||
1544 | |||
1545 | /** | ||
1546 | * We have too many active channels. Clean up the oldest channel. | ||
1547 | * | ||
1548 | * @param except channel that must NOT be cleaned up, even if it is the oldest | ||
1549 | */ | ||
1550 | static void | ||
1551 | expire_channel (struct ChannelState *except) | ||
1552 | { | ||
1553 | struct ChannelState *ts; | ||
1554 | |||
1555 | ts = GNUNET_CONTAINER_heap_peek (channel_heap); | ||
1556 | GNUNET_assert (NULL != ts); | ||
1557 | if (except == ts) | ||
1558 | return; /* can't do this */ | ||
1559 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1560 | "Tearing down expired channel to %s\n", | ||
1561 | print_channel_destination (&except->destination)); | ||
1562 | free_channel_state (ts); | ||
1563 | } | ||
1564 | |||
1565 | |||
1566 | /** | ||
1567 | * Route a packet via cadet to the given destination. | ||
1568 | * | ||
1569 | * @param destination description of the destination | ||
1570 | * @param af address family on this end (AF_INET or AF_INET6) | ||
1571 | * @param protocol IPPROTO_TCP or IPPROTO_UDP or IPPROTO_ICMP or IPPROTO_ICMPV6 | ||
1572 | * @param source_ip source IP used by the sender (struct in_addr or struct in6_addr) | ||
1573 | * @param destination_ip destination IP used by the sender (struct in_addr or struct in6_addr) | ||
1574 | * @param payload payload of the packet after the IP header | ||
1575 | * @param payload_length number of bytes in @a payload | ||
1576 | */ | ||
1577 | static void | ||
1578 | route_packet (struct DestinationEntry *destination, | ||
1579 | int af, | ||
1580 | uint8_t protocol, | ||
1581 | const void *source_ip, | ||
1582 | const void *destination_ip, | ||
1583 | const void *payload, | ||
1584 | size_t payload_length) | ||
1585 | { | ||
1586 | struct GNUNET_HashCode key; | ||
1587 | struct ChannelState *ts; | ||
1588 | size_t alen; | ||
1589 | size_t mlen; | ||
1590 | struct GNUNET_MQ_Envelope *env; | ||
1591 | const struct GNUNET_TUN_UdpHeader *udp; | ||
1592 | const struct GNUNET_TUN_TcpHeader *tcp; | ||
1593 | const struct GNUNET_TUN_IcmpHeader *icmp; | ||
1594 | struct DestinationChannel *dt; | ||
1595 | uint16_t source_port; | ||
1596 | uint16_t destination_port; | ||
1597 | |||
1598 | switch (protocol) | ||
1599 | { | ||
1600 | case IPPROTO_UDP: { | ||
1601 | if (payload_length < sizeof(struct GNUNET_TUN_UdpHeader)) | ||
1602 | { | ||
1603 | /* blame kernel? */ | ||
1604 | GNUNET_break (0); | ||
1605 | return; | ||
1606 | } | ||
1607 | tcp = NULL; /* make compiler happy */ | ||
1608 | icmp = NULL; /* make compiler happy */ | ||
1609 | udp = payload; | ||
1610 | if (udp->len < sizeof(struct GNUNET_TUN_UdpHeader)) | ||
1611 | { | ||
1612 | GNUNET_break_op (0); | ||
1613 | return; | ||
1614 | } | ||
1615 | source_port = ntohs (udp->source_port); | ||
1616 | destination_port = ntohs (udp->destination_port); | ||
1617 | get_channel_key_from_ips (af, | ||
1618 | IPPROTO_UDP, | ||
1619 | source_ip, | ||
1620 | source_port, | ||
1621 | destination_ip, | ||
1622 | destination_port, | ||
1623 | &key); | ||
1624 | } | ||
1625 | break; | ||
1626 | |||
1627 | case IPPROTO_TCP: { | ||
1628 | if (payload_length < sizeof(struct GNUNET_TUN_TcpHeader)) | ||
1629 | { | ||
1630 | /* blame kernel? */ | ||
1631 | GNUNET_break (0); | ||
1632 | return; | ||
1633 | } | ||
1634 | udp = NULL; /* make compiler happy */ | ||
1635 | icmp = NULL; /* make compiler happy */ | ||
1636 | tcp = payload; | ||
1637 | if (tcp->off * 4 < sizeof(struct GNUNET_TUN_TcpHeader)) | ||
1638 | { | ||
1639 | GNUNET_break_op (0); | ||
1640 | return; | ||
1641 | } | ||
1642 | source_port = ntohs (tcp->source_port); | ||
1643 | destination_port = ntohs (tcp->destination_port); | ||
1644 | get_channel_key_from_ips (af, | ||
1645 | IPPROTO_TCP, | ||
1646 | source_ip, | ||
1647 | source_port, | ||
1648 | destination_ip, | ||
1649 | destination_port, | ||
1650 | &key); | ||
1651 | } | ||
1652 | break; | ||
1653 | |||
1654 | case IPPROTO_ICMP: | ||
1655 | case IPPROTO_ICMPV6: { | ||
1656 | if ((AF_INET == af) ^ (protocol == IPPROTO_ICMP)) | ||
1657 | { | ||
1658 | GNUNET_break (0); | ||
1659 | return; | ||
1660 | } | ||
1661 | if (payload_length < sizeof(struct GNUNET_TUN_IcmpHeader)) | ||
1662 | { | ||
1663 | /* blame kernel? */ | ||
1664 | GNUNET_break (0); | ||
1665 | return; | ||
1666 | } | ||
1667 | tcp = NULL; /* make compiler happy */ | ||
1668 | udp = NULL; /* make compiler happy */ | ||
1669 | icmp = payload; | ||
1670 | source_port = 0; | ||
1671 | destination_port = 0; | ||
1672 | get_channel_key_from_ips (af, | ||
1673 | protocol, | ||
1674 | source_ip, | ||
1675 | 0, | ||
1676 | destination_ip, | ||
1677 | 0, | ||
1678 | &key); | ||
1679 | } | ||
1680 | break; | ||
1681 | |||
1682 | default: | ||
1683 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1684 | _ ("Protocol %u not supported, dropping\n"), | ||
1685 | (unsigned int) protocol); | ||
1686 | return; | ||
1687 | } | ||
1688 | alen = 0; | ||
1689 | if (! destination->is_service) | ||
1690 | { | ||
1691 | switch (destination->details.exit_destination.af) | ||
1692 | { | ||
1693 | case AF_INET: | ||
1694 | alen = sizeof(struct in_addr); | ||
1695 | break; | ||
1696 | |||
1697 | case AF_INET6: | ||
1698 | alen = sizeof(struct in6_addr); | ||
1699 | break; | ||
1700 | |||
1701 | default: | ||
1702 | GNUNET_assert (0); | ||
1703 | } | ||
1704 | |||
1705 | { | ||
1706 | char sbuf[INET6_ADDRSTRLEN]; | ||
1707 | char dbuf[INET6_ADDRSTRLEN]; | ||
1708 | char xbuf[INET6_ADDRSTRLEN]; | ||
1709 | |||
1710 | GNUNET_log ( | ||
1711 | GNUNET_ERROR_TYPE_DEBUG, | ||
1712 | "Routing %s packet from [%s]:%u -> [%s]:%u to destination [%s]:%u\n", | ||
1713 | (protocol == IPPROTO_TCP) ? "TCP" : "UDP", | ||
1714 | inet_ntop (af, source_ip, sbuf, sizeof(sbuf)), | ||
1715 | source_port, | ||
1716 | inet_ntop (af, destination_ip, dbuf, sizeof(dbuf)), | ||
1717 | destination_port, | ||
1718 | inet_ntop (destination->details.exit_destination.af, | ||
1719 | &destination->details.exit_destination.ip, | ||
1720 | xbuf, | ||
1721 | sizeof(xbuf)), | ||
1722 | destination_port); | ||
1723 | } | ||
1724 | for (dt = destination->dt_head; NULL != dt; dt = dt->next) | ||
1725 | if (dt->destination_port == destination_port) | ||
1726 | break; | ||
1727 | } | ||
1728 | else | ||
1729 | { | ||
1730 | { | ||
1731 | char sbuf[INET6_ADDRSTRLEN]; | ||
1732 | char dbuf[INET6_ADDRSTRLEN]; | ||
1733 | |||
1734 | GNUNET_log ( | ||
1735 | GNUNET_ERROR_TYPE_DEBUG, | ||
1736 | "Routing %s packet from [%s]:%u -> [%s]:%u to service %s at peer %s\n", | ||
1737 | (protocol == IPPROTO_TCP) ? "TCP" : "UDP", | ||
1738 | inet_ntop (af, source_ip, sbuf, sizeof(sbuf)), | ||
1739 | source_port, | ||
1740 | inet_ntop (af, destination_ip, dbuf, sizeof(dbuf)), | ||
1741 | destination_port, | ||
1742 | GNUNET_h2s ( | ||
1743 | &destination->details.service_destination.service_descriptor), | ||
1744 | GNUNET_i2s (&destination->details.service_destination.target)); | ||
1745 | } | ||
1746 | for (dt = destination->dt_head; NULL != dt; dt = dt->next) | ||
1747 | if (dt->destination_port == destination_port) | ||
1748 | break; | ||
1749 | } | ||
1750 | if (NULL == dt) | ||
1751 | { | ||
1752 | dt = GNUNET_new (struct DestinationChannel); | ||
1753 | dt->destination = destination; | ||
1754 | GNUNET_CONTAINER_DLL_insert (destination->dt_head, | ||
1755 | destination->dt_tail, | ||
1756 | dt); | ||
1757 | dt->destination_port = destination_port; | ||
1758 | } | ||
1759 | |||
1760 | /* see if we have an existing channel for this destination */ | ||
1761 | ts = GNUNET_CONTAINER_multihashmap_get (channel_map, &key); | ||
1762 | if (NULL == ts) | ||
1763 | { | ||
1764 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1765 | "Creating new channel for key %s\n", | ||
1766 | GNUNET_h2s (&key)); | ||
1767 | /* need to either use the existing channel from the destination (if still | ||
1768 | available) or create a fresh one */ | ||
1769 | ts = create_channel_to_destination (dt, af); | ||
1770 | if (NULL == ts) | ||
1771 | return; | ||
1772 | /* now bind existing "unbound" channel to our IP/port tuple */ | ||
1773 | ts->protocol = protocol; | ||
1774 | ts->af = af; | ||
1775 | if (AF_INET == af) | ||
1776 | { | ||
1777 | ts->source_ip.v4 = *(const struct in_addr *) source_ip; | ||
1778 | ts->destination_ip.v4 = *(const struct in_addr *) destination_ip; | ||
1779 | } | ||
1780 | else | ||
1781 | { | ||
1782 | ts->source_ip.v6 = *(const struct in6_addr *) source_ip; | ||
1783 | ts->destination_ip.v6 = *(const struct in6_addr *) destination_ip; | ||
1784 | } | ||
1785 | ts->source_port = source_port; | ||
1786 | ts->destination_port = destination_port; | ||
1787 | ts->heap_node = | ||
1788 | GNUNET_CONTAINER_heap_insert (channel_heap, | ||
1789 | ts, | ||
1790 | GNUNET_TIME_absolute_get ().abs_value_us); | ||
1791 | GNUNET_assert (GNUNET_YES == | ||
1792 | GNUNET_CONTAINER_multihashmap_put ( | ||
1793 | channel_map, | ||
1794 | &key, | ||
1795 | ts, | ||
1796 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
1797 | GNUNET_STATISTICS_update (stats, | ||
1798 | gettext_noop ("# Active channels"), | ||
1799 | 1, | ||
1800 | GNUNET_NO); | ||
1801 | while (GNUNET_CONTAINER_multihashmap_size (channel_map) > | ||
1802 | max_channel_mappings) | ||
1803 | expire_channel (ts); | ||
1804 | } | ||
1805 | else | ||
1806 | { | ||
1807 | GNUNET_CONTAINER_heap_update_cost (ts->heap_node, | ||
1808 | GNUNET_TIME_absolute_get () | ||
1809 | .abs_value_us); | ||
1810 | } | ||
1811 | if (NULL == ts->channel) | ||
1812 | { | ||
1813 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1814 | "Packet dropped, channel to %s not yet ready (%s)\n", | ||
1815 | print_channel_destination (&ts->destination), | ||
1816 | (NULL == ts->search) ? "EXIT search failed" | ||
1817 | : "EXIT search active"); | ||
1818 | GNUNET_STATISTICS_update (stats, | ||
1819 | gettext_noop ( | ||
1820 | "# Packets dropped (channel not yet online)"), | ||
1821 | 1, | ||
1822 | GNUNET_NO); | ||
1823 | return; | ||
1824 | } | ||
1825 | |||
1826 | /* send via channel */ | ||
1827 | switch (protocol) | ||
1828 | { | ||
1829 | case IPPROTO_UDP: | ||
1830 | if (destination->is_service) | ||
1831 | { | ||
1832 | struct GNUNET_EXIT_UdpServiceMessage *usm; | ||
1833 | |||
1834 | mlen = sizeof(struct GNUNET_EXIT_UdpServiceMessage) + payload_length | ||
1835 | - sizeof(struct GNUNET_TUN_UdpHeader); | ||
1836 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
1837 | { | ||
1838 | GNUNET_break (0); | ||
1839 | return; | ||
1840 | } | ||
1841 | env = GNUNET_MQ_msg_extra (usm, | ||
1842 | payload_length | ||
1843 | - sizeof(struct GNUNET_TUN_UdpHeader), | ||
1844 | GNUNET_MESSAGE_TYPE_VPN_UDP_TO_SERVICE); | ||
1845 | /* if the source port is below 32000, we assume it has a special | ||
1846 | meaning; if not, we pick a random port (this is a heuristic) */ | ||
1847 | usm->source_port = | ||
1848 | (ntohs (udp->source_port) < 32000) ? udp->source_port : 0; | ||
1849 | usm->destination_port = udp->destination_port; | ||
1850 | GNUNET_memcpy (&usm[1], | ||
1851 | &udp[1], | ||
1852 | payload_length - sizeof(struct GNUNET_TUN_UdpHeader)); | ||
1853 | } | ||
1854 | else | ||
1855 | { | ||
1856 | struct GNUNET_EXIT_UdpInternetMessage *uim; | ||
1857 | struct in_addr *ip4dst; | ||
1858 | struct in6_addr *ip6dst; | ||
1859 | void *payload; | ||
1860 | |||
1861 | mlen = sizeof(struct GNUNET_EXIT_UdpInternetMessage) + alen | ||
1862 | + payload_length - sizeof(struct GNUNET_TUN_UdpHeader); | ||
1863 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
1864 | { | ||
1865 | GNUNET_break (0); | ||
1866 | return; | ||
1867 | } | ||
1868 | env = GNUNET_MQ_msg_extra (uim, | ||
1869 | payload_length + alen | ||
1870 | - sizeof(struct GNUNET_TUN_UdpHeader), | ||
1871 | GNUNET_MESSAGE_TYPE_VPN_UDP_TO_INTERNET); | ||
1872 | uim->af = htonl (destination->details.exit_destination.af); | ||
1873 | uim->source_port = | ||
1874 | (ntohs (udp->source_port) < 32000) ? udp->source_port : 0; | ||
1875 | uim->destination_port = udp->destination_port; | ||
1876 | switch (destination->details.exit_destination.af) | ||
1877 | { | ||
1878 | case AF_INET: | ||
1879 | ip4dst = (struct in_addr *) &uim[1]; | ||
1880 | *ip4dst = destination->details.exit_destination.ip.v4; | ||
1881 | payload = &ip4dst[1]; | ||
1882 | break; | ||
1883 | |||
1884 | case AF_INET6: | ||
1885 | ip6dst = (struct in6_addr *) &uim[1]; | ||
1886 | *ip6dst = destination->details.exit_destination.ip.v6; | ||
1887 | payload = &ip6dst[1]; | ||
1888 | break; | ||
1889 | |||
1890 | default: | ||
1891 | GNUNET_assert (0); | ||
1892 | } | ||
1893 | GNUNET_memcpy (payload, | ||
1894 | &udp[1], | ||
1895 | payload_length - sizeof(struct GNUNET_TUN_UdpHeader)); | ||
1896 | } | ||
1897 | break; | ||
1898 | |||
1899 | case IPPROTO_TCP: | ||
1900 | if (GNUNET_NO == ts->is_established) | ||
1901 | { | ||
1902 | if (destination->is_service) | ||
1903 | { | ||
1904 | struct GNUNET_EXIT_TcpServiceStartMessage *tsm; | ||
1905 | |||
1906 | mlen = sizeof(struct GNUNET_EXIT_TcpServiceStartMessage) | ||
1907 | + payload_length - sizeof(struct GNUNET_TUN_TcpHeader); | ||
1908 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
1909 | { | ||
1910 | GNUNET_break (0); | ||
1911 | return; | ||
1912 | } | ||
1913 | env = | ||
1914 | GNUNET_MQ_msg_extra (tsm, | ||
1915 | payload_length | ||
1916 | - sizeof(struct GNUNET_TUN_TcpHeader), | ||
1917 | GNUNET_MESSAGE_TYPE_VPN_TCP_TO_SERVICE_START); | ||
1918 | tsm->reserved = htonl (0); | ||
1919 | tsm->tcp_header = *tcp; | ||
1920 | GNUNET_memcpy (&tsm[1], | ||
1921 | &tcp[1], | ||
1922 | payload_length - sizeof(struct GNUNET_TUN_TcpHeader)); | ||
1923 | } | ||
1924 | else | ||
1925 | { | ||
1926 | struct GNUNET_EXIT_TcpInternetStartMessage *tim; | ||
1927 | struct in_addr *ip4dst; | ||
1928 | struct in6_addr *ip6dst; | ||
1929 | void *payload; | ||
1930 | |||
1931 | mlen = sizeof(struct GNUNET_EXIT_TcpInternetStartMessage) + alen | ||
1932 | + payload_length - sizeof(struct GNUNET_TUN_TcpHeader); | ||
1933 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
1934 | { | ||
1935 | GNUNET_break (0); | ||
1936 | return; | ||
1937 | } | ||
1938 | env = | ||
1939 | GNUNET_MQ_msg_extra (tim, | ||
1940 | payload_length + alen | ||
1941 | - sizeof(struct GNUNET_TUN_TcpHeader), | ||
1942 | GNUNET_MESSAGE_TYPE_VPN_TCP_TO_INTERNET_START); | ||
1943 | tim->af = htonl (destination->details.exit_destination.af); | ||
1944 | tim->tcp_header = *tcp; | ||
1945 | switch (destination->details.exit_destination.af) | ||
1946 | { | ||
1947 | case AF_INET: | ||
1948 | ip4dst = (struct in_addr *) &tim[1]; | ||
1949 | *ip4dst = destination->details.exit_destination.ip.v4; | ||
1950 | payload = &ip4dst[1]; | ||
1951 | break; | ||
1952 | |||
1953 | case AF_INET6: | ||
1954 | ip6dst = (struct in6_addr *) &tim[1]; | ||
1955 | *ip6dst = destination->details.exit_destination.ip.v6; | ||
1956 | payload = &ip6dst[1]; | ||
1957 | break; | ||
1958 | |||
1959 | default: | ||
1960 | GNUNET_assert (0); | ||
1961 | } | ||
1962 | GNUNET_memcpy (payload, | ||
1963 | &tcp[1], | ||
1964 | payload_length - sizeof(struct GNUNET_TUN_TcpHeader)); | ||
1965 | } | ||
1966 | } | ||
1967 | else | ||
1968 | { | ||
1969 | struct GNUNET_EXIT_TcpDataMessage *tdm; | ||
1970 | |||
1971 | mlen = sizeof(struct GNUNET_EXIT_TcpDataMessage) + payload_length | ||
1972 | - sizeof(struct GNUNET_TUN_TcpHeader); | ||
1973 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
1974 | { | ||
1975 | GNUNET_break (0); | ||
1976 | return; | ||
1977 | } | ||
1978 | env = GNUNET_MQ_msg_extra (tdm, | ||
1979 | payload_length | ||
1980 | - sizeof(struct GNUNET_TUN_TcpHeader), | ||
1981 | GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_EXIT); | ||
1982 | tdm->reserved = htonl (0); | ||
1983 | tdm->tcp_header = *tcp; | ||
1984 | GNUNET_memcpy (&tdm[1], | ||
1985 | &tcp[1], | ||
1986 | payload_length - sizeof(struct GNUNET_TUN_TcpHeader)); | ||
1987 | } | ||
1988 | break; | ||
1989 | |||
1990 | case IPPROTO_ICMP: | ||
1991 | case IPPROTO_ICMPV6: | ||
1992 | if (destination->is_service) | ||
1993 | { | ||
1994 | struct GNUNET_EXIT_IcmpServiceMessage *ism; | ||
1995 | |||
1996 | /* ICMP protocol translation will be done by the receiver (as we don't know | ||
1997 | the target AF); however, we still need to possibly discard the payload | ||
1998 | depending on the ICMP type */ | ||
1999 | switch (af) | ||
2000 | { | ||
2001 | case AF_INET: | ||
2002 | switch (icmp->type) | ||
2003 | { | ||
2004 | case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: | ||
2005 | case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: | ||
2006 | break; | ||
2007 | |||
2008 | case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: | ||
2009 | case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: | ||
2010 | case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: | ||
2011 | /* throw away ICMP payload, won't be useful for the other side anyway */ | ||
2012 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2013 | break; | ||
2014 | |||
2015 | default: | ||
2016 | GNUNET_STATISTICS_update (stats, | ||
2017 | gettext_noop ( | ||
2018 | "# ICMPv4 packets dropped (not allowed)"), | ||
2019 | 1, | ||
2020 | GNUNET_NO); | ||
2021 | return; | ||
2022 | } | ||
2023 | /* end of AF_INET */ | ||
2024 | break; | ||
2025 | |||
2026 | case AF_INET6: | ||
2027 | switch (icmp->type) | ||
2028 | { | ||
2029 | case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: | ||
2030 | case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: | ||
2031 | case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: | ||
2032 | case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: | ||
2033 | /* throw away ICMP payload, won't be useful for the other side anyway */ | ||
2034 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2035 | break; | ||
2036 | |||
2037 | case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: | ||
2038 | case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY: | ||
2039 | break; | ||
2040 | |||
2041 | default: | ||
2042 | GNUNET_STATISTICS_update (stats, | ||
2043 | gettext_noop ( | ||
2044 | "# ICMPv6 packets dropped (not allowed)"), | ||
2045 | 1, | ||
2046 | GNUNET_NO); | ||
2047 | return; | ||
2048 | } | ||
2049 | /* end of AF_INET6 */ | ||
2050 | break; | ||
2051 | |||
2052 | default: | ||
2053 | GNUNET_assert (0); | ||
2054 | break; | ||
2055 | } | ||
2056 | |||
2057 | /* update length calculations, as payload_length may have changed */ | ||
2058 | mlen = sizeof(struct GNUNET_EXIT_IcmpServiceMessage) + alen | ||
2059 | + payload_length - sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2060 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
2061 | { | ||
2062 | GNUNET_break (0); | ||
2063 | return; | ||
2064 | } | ||
2065 | |||
2066 | env = GNUNET_MQ_msg_extra (ism, | ||
2067 | payload_length | ||
2068 | - sizeof(struct GNUNET_TUN_IcmpHeader), | ||
2069 | GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_SERVICE); | ||
2070 | ism->af = htonl (af); /* need to tell destination ICMP protocol family! */ | ||
2071 | ism->icmp_header = *icmp; | ||
2072 | GNUNET_memcpy (&ism[1], | ||
2073 | &icmp[1], | ||
2074 | payload_length - sizeof(struct GNUNET_TUN_IcmpHeader)); | ||
2075 | } | ||
2076 | else | ||
2077 | { | ||
2078 | struct GNUNET_EXIT_IcmpInternetMessage *iim; | ||
2079 | struct in_addr *ip4dst; | ||
2080 | struct in6_addr *ip6dst; | ||
2081 | void *payload; | ||
2082 | uint8_t new_type; | ||
2083 | |||
2084 | new_type = icmp->type; | ||
2085 | /* Perform ICMP protocol-translation (depending on destination AF and source AF) | ||
2086 | and throw away ICMP payload depending on ICMP message type */ | ||
2087 | switch (af) | ||
2088 | { | ||
2089 | case AF_INET: | ||
2090 | switch (icmp->type) | ||
2091 | { | ||
2092 | case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: | ||
2093 | if (destination->details.exit_destination.af == AF_INET6) | ||
2094 | new_type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY; | ||
2095 | break; | ||
2096 | |||
2097 | case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: | ||
2098 | if (destination->details.exit_destination.af == AF_INET6) | ||
2099 | new_type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST; | ||
2100 | break; | ||
2101 | |||
2102 | case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: | ||
2103 | if (destination->details.exit_destination.af == AF_INET6) | ||
2104 | new_type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE; | ||
2105 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2106 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2107 | break; | ||
2108 | |||
2109 | case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: | ||
2110 | if (destination->details.exit_destination.af == AF_INET6) | ||
2111 | new_type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED; | ||
2112 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2113 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2114 | break; | ||
2115 | |||
2116 | case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: | ||
2117 | if (destination->details.exit_destination.af == AF_INET6) | ||
2118 | { | ||
2119 | GNUNET_STATISTICS_update ( | ||
2120 | stats, | ||
2121 | gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"), | ||
2122 | 1, | ||
2123 | GNUNET_NO); | ||
2124 | return; | ||
2125 | } | ||
2126 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2127 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2128 | break; | ||
2129 | |||
2130 | default: | ||
2131 | GNUNET_STATISTICS_update ( | ||
2132 | stats, | ||
2133 | gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), | ||
2134 | 1, | ||
2135 | GNUNET_NO); | ||
2136 | return; | ||
2137 | } | ||
2138 | /* end of AF_INET */ | ||
2139 | break; | ||
2140 | |||
2141 | case AF_INET6: | ||
2142 | switch (icmp->type) | ||
2143 | { | ||
2144 | case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: | ||
2145 | if (destination->details.exit_destination.af == AF_INET) | ||
2146 | new_type = GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE; | ||
2147 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2148 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2149 | break; | ||
2150 | |||
2151 | case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: | ||
2152 | if (destination->details.exit_destination.af == AF_INET) | ||
2153 | new_type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED; | ||
2154 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2155 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2156 | break; | ||
2157 | |||
2158 | case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: | ||
2159 | if (destination->details.exit_destination.af == AF_INET) | ||
2160 | { | ||
2161 | GNUNET_STATISTICS_update ( | ||
2162 | stats, | ||
2163 | gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"), | ||
2164 | 1, | ||
2165 | GNUNET_NO); | ||
2166 | return; | ||
2167 | } | ||
2168 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2169 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2170 | break; | ||
2171 | |||
2172 | case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: | ||
2173 | if (destination->details.exit_destination.af == AF_INET) | ||
2174 | { | ||
2175 | GNUNET_STATISTICS_update ( | ||
2176 | stats, | ||
2177 | gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"), | ||
2178 | 1, | ||
2179 | GNUNET_NO); | ||
2180 | return; | ||
2181 | } | ||
2182 | /* throw away IP-payload, exit will have to make it up anyway */ | ||
2183 | payload_length = sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2184 | break; | ||
2185 | |||
2186 | case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: | ||
2187 | if (destination->details.exit_destination.af == AF_INET) | ||
2188 | new_type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST; | ||
2189 | break; | ||
2190 | |||
2191 | case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY: | ||
2192 | if (destination->details.exit_destination.af == AF_INET) | ||
2193 | new_type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY; | ||
2194 | break; | ||
2195 | |||
2196 | default: | ||
2197 | GNUNET_STATISTICS_update ( | ||
2198 | stats, | ||
2199 | gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), | ||
2200 | 1, | ||
2201 | GNUNET_NO); | ||
2202 | return; | ||
2203 | } | ||
2204 | /* end of AF_INET6 */ | ||
2205 | break; | ||
2206 | |||
2207 | default: | ||
2208 | GNUNET_assert (0); | ||
2209 | } | ||
2210 | |||
2211 | /* update length calculations, as payload_length may have changed */ | ||
2212 | mlen = sizeof(struct GNUNET_EXIT_IcmpInternetMessage) + alen | ||
2213 | + payload_length - sizeof(struct GNUNET_TUN_IcmpHeader); | ||
2214 | if (mlen >= GNUNET_MAX_MESSAGE_SIZE) | ||
2215 | { | ||
2216 | GNUNET_break (0); | ||
2217 | return; | ||
2218 | } | ||
2219 | env = GNUNET_MQ_msg_extra (iim, | ||
2220 | alen + payload_length | ||
2221 | - sizeof(struct GNUNET_TUN_IcmpHeader), | ||
2222 | GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_INTERNET); | ||
2223 | iim->icmp_header = *icmp; | ||
2224 | iim->icmp_header.type = new_type; | ||
2225 | iim->af = htonl (destination->details.exit_destination.af); | ||
2226 | switch (destination->details.exit_destination.af) | ||
2227 | { | ||
2228 | case AF_INET: | ||
2229 | ip4dst = (struct in_addr *) &iim[1]; | ||
2230 | *ip4dst = destination->details.exit_destination.ip.v4; | ||
2231 | payload = &ip4dst[1]; | ||
2232 | break; | ||
2233 | |||
2234 | case AF_INET6: | ||
2235 | ip6dst = (struct in6_addr *) &iim[1]; | ||
2236 | *ip6dst = destination->details.exit_destination.ip.v6; | ||
2237 | payload = &ip6dst[1]; | ||
2238 | break; | ||
2239 | |||
2240 | default: | ||
2241 | GNUNET_assert (0); | ||
2242 | } | ||
2243 | GNUNET_memcpy (payload, | ||
2244 | &icmp[1], | ||
2245 | payload_length - sizeof(struct GNUNET_TUN_IcmpHeader)); | ||
2246 | } | ||
2247 | break; | ||
2248 | |||
2249 | default: | ||
2250 | /* not supported above, how can we get here !? */ | ||
2251 | GNUNET_assert (0); | ||
2252 | break; | ||
2253 | } | ||
2254 | ts->is_established = GNUNET_YES; | ||
2255 | send_to_channel (ts, env); | ||
2256 | } | ||
2257 | |||
2258 | |||
2259 | /** | ||
2260 | * Receive packets from the helper-process (someone send to the local | ||
2261 | * virtual channel interface). Find the destination mapping, and if it | ||
2262 | * exists, identify the correct CADET channel (or possibly create it) | ||
2263 | * and forward the packet. | ||
2264 | * | ||
2265 | * @param cls closure, NULL | ||
2266 | * @param message message we got from the client (VPN channel interface) | ||
2267 | * @return #GNUNET_OK on success, | ||
2268 | * #GNUNET_NO to stop further processing (no error) | ||
2269 | * #GNUNET_SYSERR to stop further processing with error | ||
2270 | */ | ||
2271 | static int | ||
2272 | message_token (void *cls, const struct GNUNET_MessageHeader *message) | ||
2273 | { | ||
2274 | const struct GNUNET_TUN_Layer2PacketHeader *tun; | ||
2275 | size_t mlen; | ||
2276 | struct GNUNET_HashCode key; | ||
2277 | struct DestinationEntry *de; | ||
2278 | |||
2279 | GNUNET_STATISTICS_update (stats, | ||
2280 | gettext_noop ( | ||
2281 | "# Packets received from TUN interface"), | ||
2282 | 1, | ||
2283 | GNUNET_NO); | ||
2284 | mlen = ntohs (message->size); | ||
2285 | if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) || | ||
2286 | (mlen < sizeof(struct GNUNET_MessageHeader) | ||
2287 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader))) | ||
2288 | { | ||
2289 | GNUNET_break (0); | ||
2290 | return GNUNET_OK; | ||
2291 | } | ||
2292 | tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1]; | ||
2293 | mlen -= (sizeof(struct GNUNET_MessageHeader) | ||
2294 | + sizeof(struct GNUNET_TUN_Layer2PacketHeader)); | ||
2295 | switch (ntohs (tun->proto)) | ||
2296 | { | ||
2297 | case ETH_P_IPV6: { | ||
2298 | const struct GNUNET_TUN_IPv6Header *pkt6; | ||
2299 | |||
2300 | if (mlen < sizeof(struct GNUNET_TUN_IPv6Header)) | ||
2301 | { | ||
2302 | /* blame kernel */ | ||
2303 | GNUNET_break (0); | ||
2304 | return GNUNET_OK; | ||
2305 | } | ||
2306 | pkt6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1]; | ||
2307 | get_destination_key_from_ip (AF_INET6, &pkt6->destination_address, &key); | ||
2308 | de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key); | ||
2309 | if (NULL == de) | ||
2310 | { | ||
2311 | char buf[INET6_ADDRSTRLEN]; | ||
2312 | |||
2313 | GNUNET_log ( | ||
2314 | GNUNET_ERROR_TYPE_INFO, | ||
2315 | _ ("Packet received for unmapped destination `%s' (dropping it)\n"), | ||
2316 | inet_ntop (AF_INET6, &pkt6->destination_address, buf, sizeof(buf))); | ||
2317 | return GNUNET_OK; | ||
2318 | } | ||
2319 | route_packet (de, | ||
2320 | AF_INET6, | ||
2321 | pkt6->next_header, | ||
2322 | &pkt6->source_address, | ||
2323 | &pkt6->destination_address, | ||
2324 | &pkt6[1], | ||
2325 | mlen - sizeof(struct GNUNET_TUN_IPv6Header)); | ||
2326 | } | ||
2327 | break; | ||
2328 | |||
2329 | case ETH_P_IPV4: { | ||
2330 | struct GNUNET_TUN_IPv4Header *pkt4; | ||
2331 | |||
2332 | if (mlen < sizeof(struct GNUNET_TUN_IPv4Header)) | ||
2333 | { | ||
2334 | /* blame kernel */ | ||
2335 | GNUNET_break (0); | ||
2336 | return GNUNET_OK; | ||
2337 | } | ||
2338 | pkt4 = (struct GNUNET_TUN_IPv4Header *) &tun[1]; | ||
2339 | get_destination_key_from_ip (AF_INET, &pkt4->destination_address, &key); | ||
2340 | de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key); | ||
2341 | if (NULL == de) | ||
2342 | { | ||
2343 | char buf[INET_ADDRSTRLEN]; | ||
2344 | |||
2345 | GNUNET_log ( | ||
2346 | GNUNET_ERROR_TYPE_INFO, | ||
2347 | _ ("Packet received for unmapped destination `%s' (dropping it)\n"), | ||
2348 | inet_ntop (AF_INET, &pkt4->destination_address, buf, sizeof(buf))); | ||
2349 | return GNUNET_OK; | ||
2350 | } | ||
2351 | if (pkt4->header_length * 4 != sizeof(struct GNUNET_TUN_IPv4Header)) | ||
2352 | { | ||
2353 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
2354 | _ ("Received IPv4 packet with options (dropping it)\n")); | ||
2355 | return GNUNET_OK; | ||
2356 | } | ||
2357 | route_packet (de, | ||
2358 | AF_INET, | ||
2359 | pkt4->protocol, | ||
2360 | &pkt4->source_address, | ||
2361 | &pkt4->destination_address, | ||
2362 | &pkt4[1], | ||
2363 | mlen - sizeof(struct GNUNET_TUN_IPv4Header)); | ||
2364 | } | ||
2365 | break; | ||
2366 | |||
2367 | default: | ||
2368 | GNUNET_log ( | ||
2369 | GNUNET_ERROR_TYPE_INFO, | ||
2370 | _ ("Received packet of unknown protocol %d from TUN (dropping it)\n"), | ||
2371 | (unsigned int) ntohs (tun->proto)); | ||
2372 | break; | ||
2373 | } | ||
2374 | return GNUNET_OK; | ||
2375 | } | ||
2376 | |||
2377 | |||
2378 | /** | ||
2379 | * Allocate an IPv4 address from the range of the channel | ||
2380 | * for a new redirection. | ||
2381 | * | ||
2382 | * @param v4 where to store the address | ||
2383 | * @return #GNUNET_OK on success, | ||
2384 | * #GNUNET_SYSERR on error | ||
2385 | */ | ||
2386 | static int | ||
2387 | allocate_v4_address (struct in_addr *v4) | ||
2388 | { | ||
2389 | const char *ipv4addr = vpn_argv[4]; | ||
2390 | const char *ipv4mask = vpn_argv[5]; | ||
2391 | struct in_addr addr; | ||
2392 | struct in_addr mask; | ||
2393 | struct in_addr rnd; | ||
2394 | struct GNUNET_HashCode key; | ||
2395 | unsigned int tries; | ||
2396 | |||
2397 | GNUNET_assert (1 == inet_pton (AF_INET, ipv4addr, &addr)); | ||
2398 | GNUNET_assert (1 == inet_pton (AF_INET, ipv4mask, &mask)); | ||
2399 | /* Given 192.168.0.1/255.255.0.0, we want a mask | ||
2400 | of '192.168.255.255', thus: */ | ||
2401 | mask.s_addr = addr.s_addr | ~mask.s_addr; | ||
2402 | tries = 0; | ||
2403 | do | ||
2404 | { | ||
2405 | tries++; | ||
2406 | if (tries > 16) | ||
2407 | { | ||
2408 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2409 | _ ( | ||
2410 | "Failed to find unallocated IPv4 address in VPN's range\n")); | ||
2411 | return GNUNET_SYSERR; | ||
2412 | } | ||
2413 | /* Pick random IPv4 address within the subnet, except 'addr' or 'mask' itself */ | ||
2414 | rnd.s_addr = | ||
2415 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); | ||
2416 | v4->s_addr = (addr.s_addr | rnd.s_addr) & mask.s_addr; | ||
2417 | get_destination_key_from_ip (AF_INET, v4, &key); | ||
2418 | } | ||
2419 | while ((GNUNET_YES == | ||
2420 | GNUNET_CONTAINER_multihashmap_contains (destination_map, &key)) || | ||
2421 | (v4->s_addr == addr.s_addr) || (v4->s_addr == mask.s_addr)); | ||
2422 | return GNUNET_OK; | ||
2423 | } | ||
2424 | |||
2425 | |||
2426 | /** | ||
2427 | * Allocate an IPv6 address from the range of the channel | ||
2428 | * for a new redirection. | ||
2429 | * | ||
2430 | * @param v6 where to store the address | ||
2431 | * @return #GNUNET_OK on success, | ||
2432 | * #GNUNET_SYSERR on error | ||
2433 | */ | ||
2434 | static int | ||
2435 | allocate_v6_address (struct in6_addr *v6) | ||
2436 | { | ||
2437 | const char *ipv6addr = vpn_argv[2]; | ||
2438 | struct in6_addr addr; | ||
2439 | struct in6_addr mask; | ||
2440 | struct in6_addr rnd; | ||
2441 | int i; | ||
2442 | struct GNUNET_HashCode key; | ||
2443 | unsigned int tries; | ||
2444 | |||
2445 | GNUNET_assert (1 == inet_pton (AF_INET6, ipv6addr, &addr)); | ||
2446 | GNUNET_assert (ipv6prefix < 128); | ||
2447 | /* Given ABCD::/96, we want a mask of 'ABCD::FFFF:FFFF, | ||
2448 | thus: */ | ||
2449 | mask = addr; | ||
2450 | for (i = 127; i >= ipv6prefix; i--) | ||
2451 | mask.s6_addr[i / 8] |= (1 << (i % 8)); | ||
2452 | |||
2453 | /* Pick random IPv6 address within the subnet, except 'addr' or 'mask' itself */ | ||
2454 | tries = 0; | ||
2455 | do | ||
2456 | { | ||
2457 | tries++; | ||
2458 | if (tries > 16) | ||
2459 | { | ||
2460 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2461 | _ ( | ||
2462 | "Failed to find unallocated IPv6 address in VPN's range\n")); | ||
2463 | return GNUNET_SYSERR; | ||
2464 | } | ||
2465 | for (i = 0; i < 16; i++) | ||
2466 | { | ||
2467 | rnd.s6_addr[i] = | ||
2468 | (unsigned char) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
2469 | 256); | ||
2470 | v6->s6_addr[i] = (addr.s6_addr[i] | rnd.s6_addr[i]) & mask.s6_addr[i]; | ||
2471 | } | ||
2472 | get_destination_key_from_ip (AF_INET6, v6, &key); | ||
2473 | } | ||
2474 | while ((GNUNET_YES == | ||
2475 | GNUNET_CONTAINER_multihashmap_contains (destination_map, &key)) || | ||
2476 | (0 == GNUNET_memcmp (v6, &addr)) || | ||
2477 | (0 == GNUNET_memcmp (v6, &mask))); | ||
2478 | return GNUNET_OK; | ||
2479 | } | ||
2480 | |||
2481 | |||
2482 | /** | ||
2483 | * Free resources occupied by a destination entry. | ||
2484 | * | ||
2485 | * @param de entry to free | ||
2486 | */ | ||
2487 | static void | ||
2488 | free_destination_entry (struct DestinationEntry *de) | ||
2489 | { | ||
2490 | struct DestinationChannel *dt; | ||
2491 | |||
2492 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2493 | "Cleaning up destination entry `%s'\n", | ||
2494 | print_channel_destination (de)); | ||
2495 | GNUNET_STATISTICS_update (stats, | ||
2496 | gettext_noop ("# Active destinations"), | ||
2497 | -1, | ||
2498 | GNUNET_NO); | ||
2499 | while (NULL != (dt = de->dt_head)) | ||
2500 | { | ||
2501 | GNUNET_CONTAINER_DLL_remove (de->dt_head, de->dt_tail, dt); | ||
2502 | GNUNET_free (dt); | ||
2503 | } | ||
2504 | if (NULL != de->heap_node) | ||
2505 | { | ||
2506 | GNUNET_CONTAINER_heap_remove_node (de->heap_node); | ||
2507 | de->heap_node = NULL; | ||
2508 | GNUNET_assert ( | ||
2509 | GNUNET_YES == | ||
2510 | GNUNET_CONTAINER_multihashmap_remove (destination_map, &de->key, de)); | ||
2511 | } | ||
2512 | GNUNET_free (de); | ||
2513 | } | ||
2514 | |||
2515 | |||
2516 | /** | ||
2517 | * We have too many active destinations. Clean up the oldest destination. | ||
2518 | * | ||
2519 | * @param except destination that must NOT be cleaned up, even if it is the oldest | ||
2520 | */ | ||
2521 | static void | ||
2522 | expire_destination (struct DestinationEntry *except) | ||
2523 | { | ||
2524 | struct DestinationEntry *de; | ||
2525 | |||
2526 | de = GNUNET_CONTAINER_heap_peek (destination_heap); | ||
2527 | GNUNET_assert (NULL != de); | ||
2528 | if (except == de) | ||
2529 | return; /* can't do this */ | ||
2530 | free_destination_entry (de); | ||
2531 | } | ||
2532 | |||
2533 | |||
2534 | /** | ||
2535 | * Allocate an IP address for the response. | ||
2536 | * | ||
2537 | * @param result_af desired address family; set to the actual | ||
2538 | * address family; can initially be AF_UNSPEC if there | ||
2539 | * is no preference; will be set to AF_UNSPEC if the | ||
2540 | * allocation failed | ||
2541 | * @param addr set to either v4 or v6 depending on which | ||
2542 | * storage location was used; set to NULL if allocation failed | ||
2543 | * @param v4 storage space for an IPv4 address | ||
2544 | * @param v6 storage space for an IPv6 address | ||
2545 | * @return #GNUNET_OK normally, #GNUNET_SYSERR if `* result_af` was | ||
2546 | * an unsupported address family (not AF_INET, AF_INET6 or AF_UNSPEC) | ||
2547 | */ | ||
2548 | static int | ||
2549 | allocate_response_ip (int *result_af, | ||
2550 | void **addr, | ||
2551 | struct in_addr *v4, | ||
2552 | struct in6_addr *v6) | ||
2553 | { | ||
2554 | *addr = NULL; | ||
2555 | switch (*result_af) | ||
2556 | { | ||
2557 | case AF_INET: | ||
2558 | if (GNUNET_OK != allocate_v4_address (v4)) | ||
2559 | *result_af = AF_UNSPEC; | ||
2560 | else | ||
2561 | *addr = v4; | ||
2562 | break; | ||
2563 | |||
2564 | case AF_INET6: | ||
2565 | if (GNUNET_OK != allocate_v6_address (v6)) | ||
2566 | *result_af = AF_UNSPEC; | ||
2567 | else | ||
2568 | *addr = v6; | ||
2569 | break; | ||
2570 | |||
2571 | case AF_UNSPEC: | ||
2572 | if (GNUNET_OK == allocate_v4_address (v4)) | ||
2573 | { | ||
2574 | *addr = v4; | ||
2575 | *result_af = AF_INET; | ||
2576 | } | ||
2577 | else if (GNUNET_OK == allocate_v6_address (v6)) | ||
2578 | { | ||
2579 | *addr = v6; | ||
2580 | *result_af = AF_INET6; | ||
2581 | } | ||
2582 | break; | ||
2583 | |||
2584 | default: | ||
2585 | GNUNET_break (0); | ||
2586 | return GNUNET_SYSERR; | ||
2587 | } | ||
2588 | return GNUNET_OK; | ||
2589 | } | ||
2590 | |||
2591 | |||
2592 | /** | ||
2593 | * A client asks us to setup a redirection via some exit node to a | ||
2594 | * particular IP. Check if @a msg is well-formed. | ||
2595 | * allocated IP. | ||
2596 | * | ||
2597 | * @param cls client requesting client | ||
2598 | * @param msg redirection request | ||
2599 | * @return #GNUNET_OK if @a msg is well-formed | ||
2600 | */ | ||
2601 | static int | ||
2602 | check_client_redirect_to_ip (void *cls, | ||
2603 | const struct RedirectToIpRequestMessage *msg) | ||
2604 | { | ||
2605 | size_t alen; | ||
2606 | int addr_af; | ||
2607 | |||
2608 | alen = ntohs (msg->header.size) - sizeof(struct RedirectToIpRequestMessage); | ||
2609 | addr_af = (int) htonl (msg->addr_af); | ||
2610 | switch (addr_af) | ||
2611 | { | ||
2612 | case AF_INET: | ||
2613 | if (alen != sizeof(struct in_addr)) | ||
2614 | { | ||
2615 | GNUNET_break (0); | ||
2616 | return GNUNET_SYSERR; | ||
2617 | } | ||
2618 | break; | ||
2619 | |||
2620 | case AF_INET6: | ||
2621 | if (alen != sizeof(struct in6_addr)) | ||
2622 | { | ||
2623 | GNUNET_break (0); | ||
2624 | return GNUNET_SYSERR; | ||
2625 | } | ||
2626 | break; | ||
2627 | |||
2628 | default: | ||
2629 | GNUNET_break (0); | ||
2630 | return GNUNET_SYSERR; | ||
2631 | } | ||
2632 | return GNUNET_OK; | ||
2633 | } | ||
2634 | |||
2635 | |||
2636 | /** | ||
2637 | * A client asks us to setup a redirection via some exit node to a | ||
2638 | * particular IP. Setup the redirection and give the client the | ||
2639 | * allocated IP. | ||
2640 | * | ||
2641 | * @param cls client requesting client | ||
2642 | * @param msg redirection request | ||
2643 | */ | ||
2644 | static void | ||
2645 | handle_client_redirect_to_ip (void *cls, | ||
2646 | const struct RedirectToIpRequestMessage *msg) | ||
2647 | { | ||
2648 | struct GNUNET_SERVICE_Client *client = cls; | ||
2649 | size_t alen; | ||
2650 | int addr_af; | ||
2651 | int result_af; | ||
2652 | struct in_addr v4; | ||
2653 | struct in6_addr v6; | ||
2654 | void *addr; | ||
2655 | struct DestinationEntry *de; | ||
2656 | struct GNUNET_HashCode key; | ||
2657 | |||
2658 | alen = ntohs (msg->header.size) - sizeof(struct RedirectToIpRequestMessage); | ||
2659 | addr_af = (int) htonl (msg->addr_af); | ||
2660 | /* allocate response IP */ | ||
2661 | result_af = (int) htonl (msg->result_af); | ||
2662 | if (GNUNET_OK != allocate_response_ip (&result_af, &addr, &v4, &v6)) | ||
2663 | { | ||
2664 | GNUNET_SERVICE_client_drop (client); | ||
2665 | return; | ||
2666 | } | ||
2667 | /* send reply with our IP address */ | ||
2668 | send_client_reply (client, msg->request_id, result_af, addr); | ||
2669 | if (result_af == AF_UNSPEC) | ||
2670 | { | ||
2671 | /* failure, we're done */ | ||
2672 | GNUNET_SERVICE_client_continue (client); | ||
2673 | return; | ||
2674 | } | ||
2675 | |||
2676 | { | ||
2677 | char sbuf[INET6_ADDRSTRLEN]; | ||
2678 | char dbuf[INET6_ADDRSTRLEN]; | ||
2679 | |||
2680 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2681 | "Allocated address %s for redirection via exit to %s\n", | ||
2682 | inet_ntop (result_af, addr, sbuf, sizeof(sbuf)), | ||
2683 | inet_ntop (addr_af, &msg[1], dbuf, sizeof(dbuf))); | ||
2684 | } | ||
2685 | |||
2686 | /* setup destination record */ | ||
2687 | de = GNUNET_new (struct DestinationEntry); | ||
2688 | de->is_service = GNUNET_NO; | ||
2689 | de->details.exit_destination.af = addr_af; | ||
2690 | GNUNET_memcpy (&de->details.exit_destination.ip, &msg[1], alen); | ||
2691 | get_destination_key_from_ip (result_af, addr, &key); | ||
2692 | de->key = key; | ||
2693 | GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( | ||
2694 | destination_map, | ||
2695 | &key, | ||
2696 | de, | ||
2697 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
2698 | de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap, | ||
2699 | de, | ||
2700 | GNUNET_TIME_absolute_ntoh ( | ||
2701 | msg->expiration_time) | ||
2702 | .abs_value_us); | ||
2703 | GNUNET_STATISTICS_update (stats, | ||
2704 | gettext_noop ("# Active destinations"), | ||
2705 | 1, | ||
2706 | GNUNET_NO); | ||
2707 | while (GNUNET_CONTAINER_multihashmap_size (destination_map) > | ||
2708 | max_destination_mappings) | ||
2709 | expire_destination (de); | ||
2710 | GNUNET_SERVICE_client_continue (client); | ||
2711 | } | ||
2712 | |||
2713 | |||
2714 | /** | ||
2715 | * A client asks us to setup a redirection to a particular peer | ||
2716 | * offering a service. Setup the redirection and give the client the | ||
2717 | * allocated IP. | ||
2718 | * | ||
2719 | * @param cls requesting client | ||
2720 | * @param msg redirection request | ||
2721 | */ | ||
2722 | static void | ||
2723 | handle_client_redirect_to_service ( | ||
2724 | void *cls, | ||
2725 | const struct RedirectToServiceRequestMessage *msg) | ||
2726 | { | ||
2727 | struct GNUNET_SERVICE_Client *client = cls; | ||
2728 | int result_af; | ||
2729 | struct in_addr v4; | ||
2730 | struct in6_addr v6; | ||
2731 | void *addr; | ||
2732 | struct DestinationEntry *de; | ||
2733 | struct GNUNET_HashCode key; | ||
2734 | struct DestinationChannel *dt; | ||
2735 | |||
2736 | /* allocate response IP */ | ||
2737 | result_af = (int) htonl (msg->result_af); | ||
2738 | if (GNUNET_OK != allocate_response_ip (&result_af, &addr, &v4, &v6)) | ||
2739 | { | ||
2740 | GNUNET_break (0); | ||
2741 | GNUNET_SERVICE_client_drop (client); | ||
2742 | return; | ||
2743 | } | ||
2744 | send_client_reply (client, msg->request_id, result_af, addr); | ||
2745 | if (result_af == AF_UNSPEC) | ||
2746 | { | ||
2747 | /* failure, we're done */ | ||
2748 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2749 | _ ("Failed to allocate IP address for new destination\n")); | ||
2750 | GNUNET_SERVICE_client_continue (client); | ||
2751 | return; | ||
2752 | } | ||
2753 | |||
2754 | { | ||
2755 | char sbuf[INET6_ADDRSTRLEN]; | ||
2756 | |||
2757 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2758 | "Allocated address %s for redirection to service %s on peer %s\n", | ||
2759 | inet_ntop (result_af, addr, sbuf, sizeof(sbuf)), | ||
2760 | GNUNET_h2s (&msg->service_descriptor), | ||
2761 | GNUNET_i2s (&msg->target)); | ||
2762 | } | ||
2763 | |||
2764 | /* setup destination record */ | ||
2765 | de = GNUNET_new (struct DestinationEntry); | ||
2766 | de->is_service = GNUNET_YES; | ||
2767 | de->details.service_destination.target = msg->target; | ||
2768 | de->details.service_destination.service_descriptor = msg->service_descriptor; | ||
2769 | get_destination_key_from_ip (result_af, addr, &key); | ||
2770 | de->key = key; | ||
2771 | GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( | ||
2772 | destination_map, | ||
2773 | &key, | ||
2774 | de, | ||
2775 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
2776 | de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap, | ||
2777 | de, | ||
2778 | GNUNET_TIME_absolute_ntoh ( | ||
2779 | msg->expiration_time) | ||
2780 | .abs_value_us); | ||
2781 | while (GNUNET_CONTAINER_multihashmap_size (destination_map) > | ||
2782 | max_destination_mappings) | ||
2783 | expire_destination (de); | ||
2784 | |||
2785 | dt = GNUNET_new (struct DestinationChannel); | ||
2786 | dt->destination = de; | ||
2787 | GNUNET_CONTAINER_DLL_insert (de->dt_head, de->dt_tail, dt); | ||
2788 | /* we're done */ | ||
2789 | GNUNET_SERVICE_client_continue (client); | ||
2790 | } | ||
2791 | |||
2792 | |||
2793 | /** | ||
2794 | * Free memory occupied by an entry in the destination map. | ||
2795 | * | ||
2796 | * @param cls unused | ||
2797 | * @param key unused | ||
2798 | * @param value a `struct DestinationEntry *` | ||
2799 | * @return #GNUNET_OK (continue to iterate) | ||
2800 | */ | ||
2801 | static int | ||
2802 | cleanup_destination (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2803 | { | ||
2804 | struct DestinationEntry *de = value; | ||
2805 | |||
2806 | free_destination_entry (de); | ||
2807 | return GNUNET_OK; | ||
2808 | } | ||
2809 | |||
2810 | |||
2811 | /** | ||
2812 | * Free memory occupied by an entry in the channel map. | ||
2813 | * | ||
2814 | * @param cls unused | ||
2815 | * @param key unused | ||
2816 | * @param value a `struct ChannelState *` | ||
2817 | * @return #GNUNET_OK (continue to iterate) | ||
2818 | */ | ||
2819 | static int | ||
2820 | cleanup_channel (void *cls, const struct GNUNET_HashCode *key, void *value) | ||
2821 | { | ||
2822 | struct ChannelState *ts = value; | ||
2823 | |||
2824 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2825 | "Tearing down channel to `%s' during cleanup\n", | ||
2826 | print_channel_destination (&ts->destination)); | ||
2827 | free_channel_state (ts); | ||
2828 | return GNUNET_OK; | ||
2829 | } | ||
2830 | |||
2831 | |||
2832 | /** | ||
2833 | * Function scheduled as very last function, cleans up after us | ||
2834 | * | ||
2835 | * @param cls unused | ||
2836 | */ | ||
2837 | static void | ||
2838 | cleanup (void *cls) | ||
2839 | { | ||
2840 | unsigned int i; | ||
2841 | |||
2842 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "VPN is shutting down\n"); | ||
2843 | if (NULL != destination_map) | ||
2844 | { | ||
2845 | GNUNET_CONTAINER_multihashmap_iterate (destination_map, | ||
2846 | &cleanup_destination, | ||
2847 | NULL); | ||
2848 | GNUNET_CONTAINER_multihashmap_destroy (destination_map); | ||
2849 | destination_map = NULL; | ||
2850 | } | ||
2851 | if (NULL != destination_heap) | ||
2852 | { | ||
2853 | GNUNET_CONTAINER_heap_destroy (destination_heap); | ||
2854 | destination_heap = NULL; | ||
2855 | } | ||
2856 | if (NULL != channel_map) | ||
2857 | { | ||
2858 | GNUNET_CONTAINER_multihashmap_iterate (channel_map, &cleanup_channel, NULL); | ||
2859 | GNUNET_CONTAINER_multihashmap_destroy (channel_map); | ||
2860 | channel_map = NULL; | ||
2861 | } | ||
2862 | if (NULL != channel_heap) | ||
2863 | { | ||
2864 | GNUNET_CONTAINER_heap_destroy (channel_heap); | ||
2865 | channel_heap = NULL; | ||
2866 | } | ||
2867 | if (NULL != cadet_handle) | ||
2868 | { | ||
2869 | GNUNET_CADET_disconnect (cadet_handle); | ||
2870 | cadet_handle = NULL; | ||
2871 | } | ||
2872 | if (NULL != helper_handle) | ||
2873 | { | ||
2874 | GNUNET_HELPER_kill (helper_handle, GNUNET_NO); | ||
2875 | GNUNET_HELPER_wait (helper_handle); | ||
2876 | helper_handle = NULL; | ||
2877 | } | ||
2878 | if (NULL != stats) | ||
2879 | { | ||
2880 | GNUNET_STATISTICS_destroy (stats, GNUNET_NO); | ||
2881 | stats = NULL; | ||
2882 | } | ||
2883 | for (i = 0; i < 5; i++) | ||
2884 | GNUNET_free (vpn_argv[i]); | ||
2885 | } | ||
2886 | |||
2887 | |||
2888 | /** | ||
2889 | * Callback called when a client connects to the service. | ||
2890 | * | ||
2891 | * @param cls closure for the service | ||
2892 | * @param c the new client that connected to the service | ||
2893 | * @param mq the message queue used to send messages to the client | ||
2894 | * @return @a c | ||
2895 | */ | ||
2896 | static void * | ||
2897 | client_connect_cb (void *cls, | ||
2898 | struct GNUNET_SERVICE_Client *c, | ||
2899 | struct GNUNET_MQ_Handle *mq) | ||
2900 | { | ||
2901 | return c; | ||
2902 | } | ||
2903 | |||
2904 | |||
2905 | /** | ||
2906 | * Callback called when a client disconnected from the service | ||
2907 | * | ||
2908 | * @param cls closure for the service | ||
2909 | * @param c the client that disconnected | ||
2910 | * @param internal_cls should be equal to @a c | ||
2911 | */ | ||
2912 | static void | ||
2913 | client_disconnect_cb (void *cls, | ||
2914 | struct GNUNET_SERVICE_Client *c, | ||
2915 | void *internal_cls) | ||
2916 | { | ||
2917 | GNUNET_assert (c == internal_cls); | ||
2918 | } | ||
2919 | |||
2920 | |||
2921 | /** | ||
2922 | * Main function that will be run by the scheduler. | ||
2923 | * | ||
2924 | * @param cls closure | ||
2925 | * @param cfg_ configuration | ||
2926 | * @param service the initialized service | ||
2927 | */ | ||
2928 | static void | ||
2929 | run (void *cls, | ||
2930 | const struct GNUNET_CONFIGURATION_Handle *cfg_, | ||
2931 | struct GNUNET_SERVICE_Handle *service) | ||
2932 | { | ||
2933 | char *ifname; | ||
2934 | char *ipv6addr; | ||
2935 | char *ipv6prefix_s; | ||
2936 | char *ipv4addr; | ||
2937 | char *ipv4mask; | ||
2938 | struct in_addr v4; | ||
2939 | struct in6_addr v6; | ||
2940 | char *binary; | ||
2941 | |||
2942 | cfg = cfg_; | ||
2943 | binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-vpn"); | ||
2944 | |||
2945 | if (GNUNET_YES != | ||
2946 | GNUNET_OS_check_helper_binary ( | ||
2947 | binary, | ||
2948 | GNUNET_YES, | ||
2949 | "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0")) // ipv4 only please! | ||
2950 | { | ||
2951 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2952 | "`%s' is not SUID or the path is invalid, refusing to run.\n", | ||
2953 | binary); | ||
2954 | GNUNET_free (binary); | ||
2955 | global_ret = 1; | ||
2956 | /* we won't "really" exit here, as the 'service' is still running; | ||
2957 | however, as no handlers are registered, the service won't do | ||
2958 | anything either */ | ||
2959 | return; | ||
2960 | } | ||
2961 | stats = GNUNET_STATISTICS_create ("vpn", cfg); | ||
2962 | if (GNUNET_OK != | ||
2963 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
2964 | "VPN", | ||
2965 | "MAX_MAPPING", | ||
2966 | &max_destination_mappings)) | ||
2967 | max_destination_mappings = 200; | ||
2968 | if (GNUNET_OK != | ||
2969 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
2970 | "VPN", | ||
2971 | "MAX_TUNNELS", | ||
2972 | &max_channel_mappings)) | ||
2973 | max_channel_mappings = 200; | ||
2974 | |||
2975 | destination_map = | ||
2976 | GNUNET_CONTAINER_multihashmap_create (max_destination_mappings * 2, | ||
2977 | GNUNET_NO); | ||
2978 | destination_heap = | ||
2979 | GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
2980 | channel_map = | ||
2981 | GNUNET_CONTAINER_multihashmap_create (max_channel_mappings * 2, GNUNET_NO); | ||
2982 | channel_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
2983 | |||
2984 | |||
2985 | vpn_argv[0] = GNUNET_strdup ("vpn-gnunet"); | ||
2986 | if (GNUNET_SYSERR == | ||
2987 | GNUNET_CONFIGURATION_get_value_string (cfg, "VPN", "IFNAME", &ifname)) | ||
2988 | { | ||
2989 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "VPN", "IFNAME"); | ||
2990 | GNUNET_free (binary); | ||
2991 | GNUNET_SCHEDULER_shutdown (); | ||
2992 | return; | ||
2993 | } | ||
2994 | vpn_argv[1] = ifname; | ||
2995 | ipv6addr = NULL; | ||
2996 | if (GNUNET_OK == GNUNET_NETWORK_test_pf (PF_INET6)) | ||
2997 | { | ||
2998 | if (((GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, | ||
2999 | "VPN", | ||
3000 | "IPV6ADDR", | ||
3001 | &ipv6addr)) || | ||
3002 | (1 != inet_pton (AF_INET6, ipv6addr, &v6)))) | ||
3003 | { | ||
3004 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
3005 | "VPN", | ||
3006 | "IPV6ADDR", | ||
3007 | _ ("Must specify valid IPv6 address")); | ||
3008 | GNUNET_free (binary); | ||
3009 | GNUNET_SCHEDULER_shutdown (); | ||
3010 | GNUNET_free (ipv6addr); | ||
3011 | return; | ||
3012 | } | ||
3013 | vpn_argv[2] = ipv6addr; | ||
3014 | ipv6prefix_s = NULL; | ||
3015 | if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, | ||
3016 | "VPN", | ||
3017 | "IPV6PREFIX", | ||
3018 | &ipv6prefix_s)) | ||
3019 | { | ||
3020 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "VPN", "IPV6PREFIX"); | ||
3021 | GNUNET_SCHEDULER_shutdown (); | ||
3022 | GNUNET_free (ipv6prefix_s); | ||
3023 | return; | ||
3024 | } | ||
3025 | vpn_argv[3] = ipv6prefix_s; | ||
3026 | if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, | ||
3027 | "VPN", | ||
3028 | "IPV6PREFIX", | ||
3029 | &ipv6prefix)) || | ||
3030 | (ipv6prefix >= 127)) | ||
3031 | { | ||
3032 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
3033 | "VPN", | ||
3034 | "IPV4MASK", | ||
3035 | _ ("Must specify valid IPv6 mask")); | ||
3036 | GNUNET_free (binary); | ||
3037 | GNUNET_SCHEDULER_shutdown (); | ||
3038 | return; | ||
3039 | } | ||
3040 | } | ||
3041 | else | ||
3042 | { | ||
3043 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3044 | _ ( | ||
3045 | "IPv6 support disabled as this system does not support IPv6\n")); | ||
3046 | vpn_argv[2] = GNUNET_strdup ("-"); | ||
3047 | vpn_argv[3] = GNUNET_strdup ("-"); | ||
3048 | } | ||
3049 | if (GNUNET_OK == GNUNET_NETWORK_test_pf (PF_INET)) | ||
3050 | { | ||
3051 | ipv4addr = NULL; | ||
3052 | if (((GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, | ||
3053 | "vpn", | ||
3054 | "IPV4ADDR", | ||
3055 | &ipv4addr)) || | ||
3056 | (1 != inet_pton (AF_INET, ipv4addr, &v4)))) | ||
3057 | { | ||
3058 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
3059 | "VPN", | ||
3060 | "IPV4ADDR", | ||
3061 | _ ("Must specify valid IPv4 address")); | ||
3062 | GNUNET_free (binary); | ||
3063 | GNUNET_SCHEDULER_shutdown (); | ||
3064 | GNUNET_free (ipv4addr); | ||
3065 | return; | ||
3066 | } | ||
3067 | vpn_argv[4] = ipv4addr; | ||
3068 | ipv4mask = NULL; | ||
3069 | if (((GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, | ||
3070 | "vpn", | ||
3071 | "IPV4MASK", | ||
3072 | &ipv4mask)) || | ||
3073 | (1 != inet_pton (AF_INET, ipv4mask, &v4)))) | ||
3074 | { | ||
3075 | GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, | ||
3076 | "VPN", | ||
3077 | "IPV4MASK", | ||
3078 | _ ("Must specify valid IPv4 mask")); | ||
3079 | GNUNET_free (binary); | ||
3080 | GNUNET_SCHEDULER_shutdown (); | ||
3081 | GNUNET_free (ipv4mask); | ||
3082 | return; | ||
3083 | } | ||
3084 | vpn_argv[5] = ipv4mask; | ||
3085 | } | ||
3086 | else | ||
3087 | { | ||
3088 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
3089 | _ ( | ||
3090 | "IPv4 support disabled as this system does not support IPv4\n")); | ||
3091 | vpn_argv[4] = GNUNET_strdup ("-"); | ||
3092 | vpn_argv[5] = GNUNET_strdup ("-"); | ||
3093 | } | ||
3094 | vpn_argv[6] = NULL; | ||
3095 | |||
3096 | cadet_handle = GNUNET_CADET_connect (cfg_); | ||
3097 | // FIXME never opens ports??? | ||
3098 | helper_handle = GNUNET_HELPER_start (GNUNET_NO, | ||
3099 | binary, | ||
3100 | vpn_argv, | ||
3101 | &message_token, | ||
3102 | NULL, | ||
3103 | NULL); | ||
3104 | GNUNET_free (binary); | ||
3105 | GNUNET_SCHEDULER_add_shutdown (&cleanup, NULL); | ||
3106 | } | ||
3107 | |||
3108 | |||
3109 | /** | ||
3110 | * Define "main" method using service macro. | ||
3111 | */ | ||
3112 | GNUNET_SERVICE_MAIN ( | ||
3113 | "vpn", | ||
3114 | GNUNET_SERVICE_OPTION_NONE, | ||
3115 | &run, | ||
3116 | &client_connect_cb, | ||
3117 | &client_disconnect_cb, | ||
3118 | NULL, | ||
3119 | GNUNET_MQ_hd_var_size (client_redirect_to_ip, | ||
3120 | GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP, | ||
3121 | struct RedirectToIpRequestMessage, | ||
3122 | NULL), | ||
3123 | GNUNET_MQ_hd_fixed_size (client_redirect_to_service, | ||
3124 | GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE, | ||
3125 | struct RedirectToServiceRequestMessage, | ||
3126 | NULL), | ||
3127 | GNUNET_MQ_handler_end ()); | ||
3128 | |||
3129 | |||
3130 | /* end of gnunet-service-vpn.c */ | ||
diff --git a/src/service/vpn/meson.build b/src/service/vpn/meson.build new file mode 100644 index 000000000..ededbc68e --- /dev/null +++ b/src/service/vpn/meson.build | |||
@@ -0,0 +1,52 @@ | |||
1 | libgnunetvpn_src = ['vpn_api.c'] | ||
2 | |||
3 | gnunetservicevpn_src = ['gnunet-service-vpn.c'] | ||
4 | |||
5 | exitdir = include_directories('../exit') | ||
6 | |||
7 | configure_file(input : 'vpn.conf.in', | ||
8 | output : 'vpn.conf', | ||
9 | configuration : cdata, | ||
10 | install: true, | ||
11 | install_dir: pkgcfgdir) | ||
12 | |||
13 | |||
14 | if get_option('monolith') | ||
15 | foreach p : libgnunetvpn_src + gnunetservicevpn_src | ||
16 | gnunet_src += 'vpn/' + p | ||
17 | endforeach | ||
18 | endif | ||
19 | |||
20 | libgnunetvpn = library('gnunetvpn', | ||
21 | libgnunetvpn_src, | ||
22 | soversion: '0', | ||
23 | version: '0.0.0', | ||
24 | dependencies: libgnunetutil_dep, | ||
25 | include_directories: [incdir, configuration_inc], | ||
26 | install: true, | ||
27 | install_dir: get_option('libdir')) | ||
28 | pkg.generate(libgnunetvpn, url: 'https://www.gnunet.org', | ||
29 | description : 'Provides API for accessing the VPN service') | ||
30 | libgnunetvpn_dep = declare_dependency(link_with : libgnunetvpn) | ||
31 | |||
32 | executable ('gnunet-service-vpn', | ||
33 | gnunetservicevpn_src, | ||
34 | dependencies: [libgnunetvpn_dep, | ||
35 | libgnunetutil_dep, | ||
36 | libgnunetstatistics_dep, | ||
37 | libgnunetregex_dep, | ||
38 | libgnunetcore_dep, | ||
39 | libgnunetcadet_dep, | ||
40 | libgnunetblock_dep], | ||
41 | include_directories: [incdir, configuration_inc, exitdir], | ||
42 | install: true, | ||
43 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
44 | if host_machine.system() == 'linux' | ||
45 | executable ('gnunet-helper-vpn', | ||
46 | ['gnunet-helper-vpn.c'], | ||
47 | dependencies: [libgnunetvpn_dep, | ||
48 | libgnunetutil_dep], | ||
49 | include_directories: [incdir, configuration_inc, exitdir], | ||
50 | install: true, | ||
51 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
52 | endif | ||
diff --git a/src/service/vpn/tests/expected b/src/service/vpn/tests/expected new file mode 100644 index 000000000..26ed82af9 --- /dev/null +++ b/src/service/vpn/tests/expected | |||
Binary files differ | |||
diff --git a/src/service/vpn/tests/ping b/src/service/vpn/tests/ping new file mode 100644 index 000000000..ccc855538 --- /dev/null +++ b/src/service/vpn/tests/ping | |||
Binary files differ | |||
diff --git a/src/service/vpn/tests/test-helper-icmp.sh b/src/service/vpn/tests/test-helper-icmp.sh new file mode 100755 index 000000000..2d0c8425a --- /dev/null +++ b/src/service/vpn/tests/test-helper-icmp.sh | |||
@@ -0,0 +1,17 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | /opt/gnunet/bin/gnunet-helper-vpn < ping > result 2>/dev/null & | ||
4 | |||
5 | PID=$! | ||
6 | |||
7 | sleep 1 | ||
8 | |||
9 | kill $PID | ||
10 | |||
11 | if cmp result expected; then | ||
12 | echo OK | ||
13 | exit 0 | ||
14 | else | ||
15 | echo FAILED: ICMP-Reply | ||
16 | exit 1 | ||
17 | fi | ||
diff --git a/src/service/vpn/tests/test-helper-ifaddr.sh b/src/service/vpn/tests/test-helper-ifaddr.sh new file mode 100755 index 000000000..b8fde999d --- /dev/null +++ b/src/service/vpn/tests/test-helper-ifaddr.sh | |||
@@ -0,0 +1,41 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | FIFO=$(mktemp) | ||
4 | |||
5 | rm $FIFO | ||
6 | |||
7 | mkfifo $FIFO | ||
8 | |||
9 | /opt/gnunet/bin/gnunet-helper-vpn > $FIFO 2>&1 & | ||
10 | |||
11 | PID=$! | ||
12 | |||
13 | sleep 1 | ||
14 | |||
15 | IF="" | ||
16 | while read line < $FIFO; do | ||
17 | IF=$(echo $line | grep interface | sed -e 's/.*interface \([^ ]*\).*/\1/') | ||
18 | if [ "$IF" != "" ]; then | ||
19 | break | ||
20 | fi | ||
21 | done | ||
22 | |||
23 | r=0 | ||
24 | if /sbin/ifconfig $IF | grep inet6 | grep -q '1234::1/16'; then | ||
25 | echo OK | ||
26 | else | ||
27 | echo FAILED: Interface-Address not set for IPv6! | ||
28 | r=1 | ||
29 | fi | ||
30 | |||
31 | if /sbin/ifconfig $IF | grep "inet " | grep -q '10.10.10.1'; then | ||
32 | echo OK | ||
33 | else | ||
34 | echo FAILED: Interface-Address not set for IPv4! | ||
35 | r=1 | ||
36 | fi | ||
37 | |||
38 | rm $FIFO | ||
39 | kill $PID | ||
40 | |||
41 | exit $r | ||
diff --git a/src/service/vpn/vpn.conf.in b/src/service/vpn/vpn.conf.in new file mode 100644 index 000000000..c0f4c59ed --- /dev/null +++ b/src/service/vpn/vpn.conf.in | |||
@@ -0,0 +1,19 @@ | |||
1 | [vpn] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @UNIXONLY@ PORT = 2105 | ||
4 | HOSTNAME = localhost | ||
5 | BINARY = gnunet-service-vpn | ||
6 | ACCEPT_FROM = 127.0.0.1; | ||
7 | ACCEPT_FROM6 = ::1; | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-vpn.sock | ||
9 | UNIX_MATCH_UID = NO | ||
10 | UNIX_MATCH_GID = YES | ||
11 | |||
12 | IPV6ADDR = 1234::1 | ||
13 | IPV6PREFIX = 32 | ||
14 | IPV4ADDR = 10.11.10.1 | ||
15 | IPV4MASK = 255.255.0.0 | ||
16 | VIRTDNS = 10.11.10.2 | ||
17 | VIRTDNS6 = 1234::17 | ||
18 | IFNAME = vpn-gnunet | ||
19 | |||
diff --git a/src/service/vpn/vpn.h b/src/service/vpn/vpn.h new file mode 100644 index 000000000..77cfd34e6 --- /dev/null +++ b/src/service/vpn/vpn.h | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012 Christian Grothoff | ||
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 | |||
21 | /** | ||
22 | * @file vpn/vpn.h | ||
23 | * @brief IPC messages between VPN library and VPN service | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef VPN_H | ||
27 | #define VPN_H | ||
28 | |||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | GNUNET_NETWORK_STRUCT_BEGIN | ||
32 | |||
33 | /** | ||
34 | * Message send by the VPN client to the VPN service requesting | ||
35 | * the setup of a redirection from some IP via an exit node to | ||
36 | * some global Internet address. | ||
37 | */ | ||
38 | struct RedirectToIpRequestMessage | ||
39 | { | ||
40 | /** | ||
41 | * Type is #GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP | ||
42 | */ | ||
43 | struct GNUNET_MessageHeader header; | ||
44 | |||
45 | /** | ||
46 | * Always zero. | ||
47 | */ | ||
48 | uint32_t reserved GNUNET_PACKED; | ||
49 | |||
50 | /** | ||
51 | * How long should the redirection be maintained at most? | ||
52 | */ | ||
53 | struct GNUNET_TIME_AbsoluteNBO expiration_time; | ||
54 | |||
55 | /** | ||
56 | * Address family desired for the result (AF_INET or AF_INET6 or AF_UNSPEC, in nbo) | ||
57 | */ | ||
58 | int32_t result_af GNUNET_PACKED; | ||
59 | |||
60 | /** | ||
61 | * Address family used for the destination address (AF_INET or AF_INET6, in nbo) | ||
62 | */ | ||
63 | int32_t addr_af GNUNET_PACKED; | ||
64 | |||
65 | /** | ||
66 | * Unique ID to match a future response to this request. | ||
67 | * Picked by the client. | ||
68 | */ | ||
69 | uint64_t request_id GNUNET_PACKED; | ||
70 | |||
71 | /* followed by destination address ('struct in_addr' or 'struct in6_addr') */ | ||
72 | }; | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Message send by the VPN client to the VPN service requesting | ||
77 | * the setup of a redirection from some IP to a service running | ||
78 | * at a particular peer. | ||
79 | */ | ||
80 | struct RedirectToServiceRequestMessage | ||
81 | { | ||
82 | /** | ||
83 | * Type is #GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE | ||
84 | */ | ||
85 | struct GNUNET_MessageHeader header; | ||
86 | |||
87 | /** | ||
88 | * Always zero. | ||
89 | */ | ||
90 | uint32_t reserved GNUNET_PACKED; | ||
91 | |||
92 | /** | ||
93 | * How long should the redirection be maintained at most? | ||
94 | */ | ||
95 | struct GNUNET_TIME_AbsoluteNBO expiration_time; | ||
96 | |||
97 | /** | ||
98 | * Desired protocol (IPPROTO_UDP or IPPROTO_TCP) | ||
99 | */ | ||
100 | int32_t protocol GNUNET_PACKED; | ||
101 | |||
102 | /** | ||
103 | * Address family desired for the result (AF_INET or AF_INET6 or AF_UNSPEC, in nbo) | ||
104 | */ | ||
105 | int32_t result_af GNUNET_PACKED; | ||
106 | |||
107 | /** | ||
108 | * Target peer offering the service. | ||
109 | */ | ||
110 | struct GNUNET_PeerIdentity target; | ||
111 | |||
112 | /** | ||
113 | * Service descriptor identifying the service. | ||
114 | */ | ||
115 | struct GNUNET_HashCode service_descriptor; | ||
116 | |||
117 | /** | ||
118 | * Unique ID to match a future response to this request. | ||
119 | * Picked by the client. | ||
120 | */ | ||
121 | uint64_t request_id GNUNET_PACKED; | ||
122 | }; | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Response from the VPN service to a VPN client informing about | ||
127 | * the IP that was assigned for the requested redirection. | ||
128 | */ | ||
129 | struct RedirectToIpResponseMessage | ||
130 | { | ||
131 | /** | ||
132 | * Type is #GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP | ||
133 | */ | ||
134 | struct GNUNET_MessageHeader header; | ||
135 | |||
136 | /** | ||
137 | * Address family of the allocated address that follows; will match | ||
138 | * "result_af" from the request, of be "AF_UNSPEC" on errors. | ||
139 | */ | ||
140 | int32_t result_af GNUNET_PACKED; | ||
141 | |||
142 | /** | ||
143 | * Unique ID to match the response to a request. | ||
144 | */ | ||
145 | uint64_t request_id GNUNET_PACKED; | ||
146 | |||
147 | /* followed by destination address ('struct in_addr' or 'struct in6_addr') */ | ||
148 | }; | ||
149 | |||
150 | GNUNET_NETWORK_STRUCT_END | ||
151 | |||
152 | |||
153 | #endif | ||
diff --git a/src/service/vpn/vpn_api.c b/src/service/vpn/vpn_api.c new file mode 100644 index 000000000..8a3464786 --- /dev/null +++ b/src/service/vpn/vpn_api.c | |||
@@ -0,0 +1,529 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012, 2016 Christian Grothoff | ||
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 | |||
21 | /** | ||
22 | * @file vpn/vpn_api.c | ||
23 | * @brief library to access the VPN service and tell it how to redirect traffic | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_vpn_service.h" | ||
28 | #include "vpn.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Opaque VPN handle | ||
33 | */ | ||
34 | struct GNUNET_VPN_Handle | ||
35 | { | ||
36 | /** | ||
37 | * Configuration we use. | ||
38 | */ | ||
39 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
40 | |||
41 | /** | ||
42 | * Connection to VPN service. | ||
43 | */ | ||
44 | struct GNUNET_MQ_Handle *mq; | ||
45 | |||
46 | /** | ||
47 | * Head of list of active redirection requests. | ||
48 | */ | ||
49 | struct GNUNET_VPN_RedirectionRequest *rr_head; | ||
50 | |||
51 | /** | ||
52 | * Tail of list of active redirection requests. | ||
53 | */ | ||
54 | struct GNUNET_VPN_RedirectionRequest *rr_tail; | ||
55 | |||
56 | /** | ||
57 | * Identifier of a reconnect task. | ||
58 | */ | ||
59 | struct GNUNET_SCHEDULER_Task *rt; | ||
60 | |||
61 | /** | ||
62 | * How long do we wait until we try to reconnect? | ||
63 | */ | ||
64 | struct GNUNET_TIME_Relative backoff; | ||
65 | |||
66 | /** | ||
67 | * ID of the last request that was submitted to the service. | ||
68 | */ | ||
69 | uint64_t request_id_gen; | ||
70 | }; | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Opaque redirection request handle. | ||
75 | */ | ||
76 | struct GNUNET_VPN_RedirectionRequest | ||
77 | { | ||
78 | /** | ||
79 | * Element in DLL. | ||
80 | */ | ||
81 | struct GNUNET_VPN_RedirectionRequest *next; | ||
82 | |||
83 | /** | ||
84 | * Element in DLL. | ||
85 | */ | ||
86 | struct GNUNET_VPN_RedirectionRequest *prev; | ||
87 | |||
88 | /** | ||
89 | * Pointer to the VPN struct. | ||
90 | */ | ||
91 | struct GNUNET_VPN_Handle *vh; | ||
92 | |||
93 | /** | ||
94 | * Target IP address for the redirection, or NULL for | ||
95 | * redirection to service. Allocated after this struct. | ||
96 | */ | ||
97 | const void *addr; | ||
98 | |||
99 | /** | ||
100 | * Function to call with the designated IP address. | ||
101 | */ | ||
102 | GNUNET_VPN_AllocationCallback cb; | ||
103 | |||
104 | /** | ||
105 | * Closure for @e cb. | ||
106 | */ | ||
107 | void *cb_cls; | ||
108 | |||
109 | /** | ||
110 | * For service redirection, identity of the peer offering the service. | ||
111 | */ | ||
112 | struct GNUNET_PeerIdentity peer; | ||
113 | |||
114 | /** | ||
115 | * For service redirection, service descriptor. | ||
116 | */ | ||
117 | struct GNUNET_HashCode serv; | ||
118 | |||
119 | /** | ||
120 | * At what time should the created service mapping expire? | ||
121 | */ | ||
122 | struct GNUNET_TIME_Absolute expiration_time; | ||
123 | |||
124 | /** | ||
125 | * non-zero if this request has been sent to the service. | ||
126 | */ | ||
127 | uint64_t request_id; | ||
128 | |||
129 | /** | ||
130 | * Desired address family for the result. | ||
131 | */ | ||
132 | int result_af; | ||
133 | |||
134 | /** | ||
135 | * Address family of @e addr. AF_INET or AF_INET6. | ||
136 | */ | ||
137 | int addr_af; | ||
138 | |||
139 | /** | ||
140 | * For service redirection, IPPROT_UDP or IPPROTO_TCP. | ||
141 | */ | ||
142 | uint8_t protocol; | ||
143 | }; | ||
144 | |||
145 | |||
146 | /** | ||
147 | * Disconnect from the service (communication error) and reconnect later. | ||
148 | * | ||
149 | * @param vh handle to reconnect. | ||
150 | */ | ||
151 | static void | ||
152 | reconnect (struct GNUNET_VPN_Handle *vh); | ||
153 | |||
154 | |||
155 | /** | ||
156 | * Check a #GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP message from the | ||
157 | * VPN service. | ||
158 | * | ||
159 | * @param cls the `struct GNUNET_VPN_Handle` | ||
160 | * @param rm message received | ||
161 | * @return #GNUNET_OK if @a rm is well-formed | ||
162 | */ | ||
163 | static int | ||
164 | check_use_ip (void *cls, | ||
165 | const struct RedirectToIpResponseMessage *rm) | ||
166 | { | ||
167 | size_t alen; | ||
168 | int af; | ||
169 | |||
170 | af = (int) ntohl (rm->result_af); | ||
171 | switch (af) | ||
172 | { | ||
173 | case AF_UNSPEC: | ||
174 | alen = 0; | ||
175 | break; | ||
176 | |||
177 | case AF_INET: | ||
178 | alen = sizeof(struct in_addr); | ||
179 | break; | ||
180 | |||
181 | case AF_INET6: | ||
182 | alen = sizeof(struct in6_addr); | ||
183 | break; | ||
184 | |||
185 | default: | ||
186 | GNUNET_break (0); | ||
187 | return GNUNET_SYSERR; | ||
188 | } | ||
189 | if ((ntohs (rm->header.size) != alen + sizeof(*rm)) || | ||
190 | (0 == rm->request_id)) | ||
191 | { | ||
192 | GNUNET_break (0); | ||
193 | return GNUNET_SYSERR; | ||
194 | } | ||
195 | return GNUNET_OK; | ||
196 | } | ||
197 | |||
198 | |||
199 | /** | ||
200 | * Handle a #GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP message from the | ||
201 | * VPN service. | ||
202 | * | ||
203 | * @param cls the `struct GNUNET_VPN_Handle` | ||
204 | * @param rm message received | ||
205 | */ | ||
206 | static void | ||
207 | handle_use_ip (void *cls, | ||
208 | const struct RedirectToIpResponseMessage *rm) | ||
209 | { | ||
210 | struct GNUNET_VPN_Handle *vh = cls; | ||
211 | struct GNUNET_VPN_RedirectionRequest *rr; | ||
212 | int af; | ||
213 | |||
214 | af = (int) ntohl (rm->result_af); | ||
215 | for (rr = vh->rr_head; NULL != rr; rr = rr->next) | ||
216 | { | ||
217 | if (rr->request_id == rm->request_id) | ||
218 | { | ||
219 | GNUNET_CONTAINER_DLL_remove (vh->rr_head, | ||
220 | vh->rr_tail, | ||
221 | rr); | ||
222 | rr->cb (rr->cb_cls, | ||
223 | af, | ||
224 | (af == AF_UNSPEC) ? NULL : &rm[1]); | ||
225 | GNUNET_free (rr); | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Add a request to our request queue and transmit it. | ||
234 | * | ||
235 | * @param rr request to queue and transmit. | ||
236 | */ | ||
237 | static void | ||
238 | send_request (struct GNUNET_VPN_RedirectionRequest *rr) | ||
239 | { | ||
240 | struct GNUNET_VPN_Handle *vh = rr->vh; | ||
241 | struct RedirectToIpRequestMessage *rip; | ||
242 | struct RedirectToServiceRequestMessage *rs; | ||
243 | struct GNUNET_MQ_Envelope *env; | ||
244 | size_t alen; | ||
245 | |||
246 | if (NULL == vh->mq) | ||
247 | return; | ||
248 | if (NULL == rr->addr) | ||
249 | { | ||
250 | env = GNUNET_MQ_msg (rs, | ||
251 | GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE); | ||
252 | rs->reserved = htonl (0); | ||
253 | rs->expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time); | ||
254 | rs->protocol = htonl (rr->protocol); | ||
255 | rs->result_af = htonl (rr->result_af); | ||
256 | rs->target = rr->peer; | ||
257 | rs->service_descriptor = rr->serv; | ||
258 | rs->request_id = rr->request_id = ++vh->request_id_gen; | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | switch (rr->addr_af) | ||
263 | { | ||
264 | case AF_INET: | ||
265 | alen = sizeof(struct in_addr); | ||
266 | break; | ||
267 | |||
268 | case AF_INET6: | ||
269 | alen = sizeof(struct in6_addr); | ||
270 | break; | ||
271 | |||
272 | default: | ||
273 | GNUNET_assert (0); | ||
274 | return; | ||
275 | } | ||
276 | env = GNUNET_MQ_msg_extra (rip, | ||
277 | alen, | ||
278 | GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP); | ||
279 | rip->reserved = htonl (0); | ||
280 | rip->expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time); | ||
281 | rip->result_af = htonl (rr->result_af); | ||
282 | rip->addr_af = htonl (rr->addr_af); | ||
283 | rip->request_id = rr->request_id = ++vh->request_id_gen; | ||
284 | GNUNET_memcpy (&rip[1], | ||
285 | rr->addr, | ||
286 | alen); | ||
287 | } | ||
288 | GNUNET_MQ_send (vh->mq, | ||
289 | env); | ||
290 | } | ||
291 | |||
292 | |||
293 | /** | ||
294 | * Generic error handler, called with the appropriate error code and | ||
295 | * the same closure specified at the creation of the message queue. | ||
296 | * Not every message queue implementation supports an error handler. | ||
297 | * | ||
298 | * @param cls closure with the `struct GNUNET_VPN_Handle *` | ||
299 | * @param error error code | ||
300 | */ | ||
301 | static void | ||
302 | mq_error_handler (void *cls, | ||
303 | enum GNUNET_MQ_Error error) | ||
304 | { | ||
305 | struct GNUNET_VPN_Handle *vh = cls; | ||
306 | |||
307 | reconnect (vh); | ||
308 | } | ||
309 | |||
310 | |||
311 | /** | ||
312 | * Connect to the VPN service and start again to transmit our requests. | ||
313 | * | ||
314 | * @param cls the `struct GNUNET_VPN_Handle *` | ||
315 | */ | ||
316 | static void | ||
317 | connect_task (void *cls) | ||
318 | { | ||
319 | struct GNUNET_VPN_Handle *vh = cls; | ||
320 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
321 | GNUNET_MQ_hd_var_size (use_ip, | ||
322 | GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP, | ||
323 | struct RedirectToIpResponseMessage, | ||
324 | cls), | ||
325 | GNUNET_MQ_handler_end () | ||
326 | }; | ||
327 | struct GNUNET_VPN_RedirectionRequest *rr; | ||
328 | |||
329 | vh->rt = NULL; | ||
330 | vh->mq = GNUNET_CLIENT_connect (vh->cfg, | ||
331 | "vpn", | ||
332 | handlers, | ||
333 | &mq_error_handler, | ||
334 | vh); | ||
335 | if (NULL == vh->mq) | ||
336 | return; | ||
337 | for (rr = vh->rr_head; NULL != rr; rr = rr->next) | ||
338 | send_request (rr); | ||
339 | } | ||
340 | |||
341 | |||
342 | /** | ||
343 | * Disconnect from the service (communication error) and reconnect later. | ||
344 | * | ||
345 | * @param vh handle to reconnect. | ||
346 | */ | ||
347 | static void | ||
348 | reconnect (struct GNUNET_VPN_Handle *vh) | ||
349 | { | ||
350 | struct GNUNET_VPN_RedirectionRequest *rr; | ||
351 | |||
352 | GNUNET_MQ_destroy (vh->mq); | ||
353 | vh->mq = NULL; | ||
354 | vh->request_id_gen = 0; | ||
355 | for (rr = vh->rr_head; NULL != rr; rr = rr->next) | ||
356 | rr->request_id = 0; | ||
357 | vh->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, | ||
358 | GNUNET_TIME_relative_min ( | ||
359 | GNUNET_TIME_relative_saturating_multiply ( | ||
360 | vh->backoff, 2), | ||
361 | GNUNET_TIME_relative_multiply ( | ||
362 | GNUNET_TIME_UNIT_SECONDS, 30))); | ||
363 | vh->rt = GNUNET_SCHEDULER_add_delayed (vh->backoff, | ||
364 | &connect_task, | ||
365 | vh); | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * Cancel redirection request with the service. | ||
371 | * | ||
372 | * @param rr request to cancel | ||
373 | */ | ||
374 | void | ||
375 | GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr) | ||
376 | { | ||
377 | struct GNUNET_VPN_Handle *vh; | ||
378 | |||
379 | vh = rr->vh; | ||
380 | GNUNET_CONTAINER_DLL_remove (vh->rr_head, | ||
381 | vh->rr_tail, | ||
382 | rr); | ||
383 | GNUNET_free (rr); | ||
384 | } | ||
385 | |||
386 | |||
387 | struct GNUNET_VPN_RedirectionRequest * | ||
388 | GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh, | ||
389 | int result_af, | ||
390 | uint8_t protocol, | ||
391 | const struct GNUNET_PeerIdentity *peer, | ||
392 | const struct GNUNET_HashCode *serv, | ||
393 | struct GNUNET_TIME_Absolute expiration_time, | ||
394 | GNUNET_VPN_AllocationCallback cb, | ||
395 | void *cb_cls) | ||
396 | { | ||
397 | struct GNUNET_VPN_RedirectionRequest *rr; | ||
398 | |||
399 | rr = GNUNET_new (struct GNUNET_VPN_RedirectionRequest); | ||
400 | rr->vh = vh; | ||
401 | rr->cb = cb; | ||
402 | rr->cb_cls = cb_cls; | ||
403 | rr->peer = *peer; | ||
404 | rr->serv = *serv; | ||
405 | rr->expiration_time = expiration_time; | ||
406 | rr->result_af = result_af; | ||
407 | rr->protocol = protocol; | ||
408 | GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head, | ||
409 | vh->rr_tail, | ||
410 | rr); | ||
411 | send_request (rr); | ||
412 | return rr; | ||
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Tell the VPN that forwarding to the Internet via some exit node is | ||
418 | * requested. Note that both UDP and TCP traffic will be forwarded, | ||
419 | * but possibly to different exit nodes. The VPN is to reserve a | ||
420 | * particular IP for the redirection and return it. The VPN will | ||
421 | * begin the redirection as soon as possible and maintain it as long | ||
422 | * as it is actively used and keeping it is feasible. Given resource | ||
423 | * limitations, the longest inactive mappings will be destroyed. | ||
424 | * | ||
425 | * @param vh VPN handle | ||
426 | * @param result_af desired address family for the returned allocation | ||
427 | * @param addr_af address family for @a addr, AF_INET or AF_INET6 | ||
428 | * @param addr destination IP address on the Internet; destination | ||
429 | * port is to be taken from the VPN packet itself | ||
430 | * @param expiration_time at what time should the redirection expire? | ||
431 | * (this should not impact connections that are active at that time) | ||
432 | * @param cb function to call with the IP | ||
433 | * @param cb_cls closure for @a cb | ||
434 | * @return handle to cancel the request (means the callback won't be | ||
435 | * invoked anymore; the mapping may or may not be established | ||
436 | * anyway) | ||
437 | */ | ||
438 | struct GNUNET_VPN_RedirectionRequest * | ||
439 | GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh, | ||
440 | int result_af, | ||
441 | int addr_af, | ||
442 | const void *addr, | ||
443 | struct GNUNET_TIME_Absolute expiration_time, | ||
444 | GNUNET_VPN_AllocationCallback cb, | ||
445 | void *cb_cls) | ||
446 | { | ||
447 | struct GNUNET_VPN_RedirectionRequest *rr; | ||
448 | size_t alen; | ||
449 | |||
450 | switch (addr_af) | ||
451 | { | ||
452 | case AF_INET: | ||
453 | alen = sizeof(struct in_addr); | ||
454 | break; | ||
455 | |||
456 | case AF_INET6: | ||
457 | alen = sizeof(struct in6_addr); | ||
458 | break; | ||
459 | |||
460 | default: | ||
461 | GNUNET_break (0); | ||
462 | return NULL; | ||
463 | } | ||
464 | rr = GNUNET_malloc (sizeof(struct GNUNET_VPN_RedirectionRequest) + alen); | ||
465 | rr->vh = vh; | ||
466 | rr->addr = &rr[1]; | ||
467 | rr->cb = cb; | ||
468 | rr->cb_cls = cb_cls; | ||
469 | rr->expiration_time = expiration_time; | ||
470 | rr->result_af = result_af; | ||
471 | rr->addr_af = addr_af; | ||
472 | GNUNET_memcpy (&rr[1], | ||
473 | addr, | ||
474 | alen); | ||
475 | GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head, | ||
476 | vh->rr_tail, | ||
477 | rr); | ||
478 | send_request (rr); | ||
479 | return rr; | ||
480 | } | ||
481 | |||
482 | |||
483 | /** | ||
484 | * Connect to the VPN service | ||
485 | * | ||
486 | * @param cfg configuration to use | ||
487 | * @return VPN handle | ||
488 | */ | ||
489 | struct GNUNET_VPN_Handle * | ||
490 | GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
491 | { | ||
492 | struct GNUNET_VPN_Handle *vh | ||
493 | = GNUNET_new (struct GNUNET_VPN_Handle); | ||
494 | |||
495 | vh->cfg = cfg; | ||
496 | connect_task (vh); | ||
497 | if (NULL == vh->mq) | ||
498 | { | ||
499 | GNUNET_free (vh); | ||
500 | return NULL; | ||
501 | } | ||
502 | return vh; | ||
503 | } | ||
504 | |||
505 | |||
506 | /** | ||
507 | * Disconnect from the VPN service. | ||
508 | * | ||
509 | * @param vh VPN handle | ||
510 | */ | ||
511 | void | ||
512 | GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh) | ||
513 | { | ||
514 | GNUNET_assert (NULL == vh->rr_head); | ||
515 | if (NULL != vh->mq) | ||
516 | { | ||
517 | GNUNET_MQ_destroy (vh->mq); | ||
518 | vh->mq = NULL; | ||
519 | } | ||
520 | if (NULL != vh->rt) | ||
521 | { | ||
522 | GNUNET_SCHEDULER_cancel (vh->rt); | ||
523 | vh->rt = NULL; | ||
524 | } | ||
525 | GNUNET_free (vh); | ||
526 | } | ||
527 | |||
528 | |||
529 | /* end of vpn_api.c */ | ||