diff options
author | Christian Grothoff <christian@grothoff.org> | 2010-02-03 16:29:30 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2010-02-03 16:29:30 +0000 |
commit | 4fe3386a587534dcfcb82ddf885a40ac93e603ca (patch) | |
tree | e155b5f76325d715800e85d77946bc7663b9463a /src | |
parent | cb3cae418d620e523db553f7607832531a58ed71 (diff) | |
download | gnunet-4fe3386a587534dcfcb82ddf885a40ac93e603ca.tar.gz gnunet-4fe3386a587534dcfcb82ddf885a40ac93e603ca.zip |
gns draft
Diffstat (limited to 'src')
-rw-r--r-- | src/transport/Makefile.am | 7 | ||||
-rw-r--r-- | src/transport/gnunet-nat-server.c | 488 |
2 files changed, 494 insertions, 1 deletions
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 5d19525b9..0580ad603 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am | |||
@@ -29,7 +29,12 @@ libgnunettransport_la_LDFLAGS = \ | |||
29 | 29 | ||
30 | bin_PROGRAMS = \ | 30 | bin_PROGRAMS = \ |
31 | gnunet-transport \ | 31 | gnunet-transport \ |
32 | gnunet-service-transport | 32 | gnunet-service-transport \ |
33 | gnunet-nat-server | ||
34 | |||
35 | gnunet_nat_server_SOURCES = \ | ||
36 | gnunet-nat-server.c | ||
37 | |||
33 | 38 | ||
34 | gnunet_transport_SOURCES = \ | 39 | gnunet_transport_SOURCES = \ |
35 | gnunet-transport.c | 40 | gnunet-transport.c |
diff --git a/src/transport/gnunet-nat-server.c b/src/transport/gnunet-nat-server.c new file mode 100644 index 000000000..88674fd2c --- /dev/null +++ b/src/transport/gnunet-nat-server.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2010 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | 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 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/transport/gnunet-nat-server.c | ||
23 | * @brief Tool to help bypass NATs using ICMP method; must run as root (for now, later SUID will do) | ||
24 | * This code will work under GNU/Linux only (or maybe BSDs, but never W32) | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | |||
28 | #include <sys/types.h> | ||
29 | #include <sys/socket.h> | ||
30 | #include <arpa/inet.h> | ||
31 | #include <sys/select.h> | ||
32 | #include <sys/time.h> | ||
33 | #include <sys/types.h> | ||
34 | #include <unistd.h> | ||
35 | #include <stdio.h> | ||
36 | #include <string.h> | ||
37 | #include <errno.h> | ||
38 | #include <stdlib.h> | ||
39 | #include <stdint.h> | ||
40 | #include <netinet/ip.h> | ||
41 | #include <netinet/ip_icmp.h> | ||
42 | #include <netinet/in.h> | ||
43 | |||
44 | /** | ||
45 | * Number of UDP ports to keep open. | ||
46 | */ | ||
47 | #define NUM_UDP_PORTS 512 | ||
48 | |||
49 | /** | ||
50 | * How often do we send our UDP messages to keep ports open? | ||
51 | */ | ||
52 | #define UDP_SEND_FREQUENCY_MS 50 | ||
53 | |||
54 | /** | ||
55 | * Port we use for the dummy target. | ||
56 | */ | ||
57 | #define NAT_TRAV_PORT 2222 | ||
58 | |||
59 | /** | ||
60 | * How often do we retry to open and bind a UDP socket before giving up? | ||
61 | */ | ||
62 | #define MAX_TRIES 10 | ||
63 | |||
64 | |||
65 | struct ip_packet | ||
66 | { | ||
67 | |||
68 | uint8_t vers_ihl; | ||
69 | uint8_t tos; | ||
70 | uint16_t pkt_len; | ||
71 | uint16_t id; | ||
72 | uint16_t flags_frag_offset; | ||
73 | uint8_t ttl; | ||
74 | uint8_t proto; | ||
75 | uint16_t checksum; | ||
76 | uint32_t src_ip; | ||
77 | uint32_t dst_ip; | ||
78 | }; | ||
79 | |||
80 | struct icmp_packet | ||
81 | { | ||
82 | uint8_t type; | ||
83 | uint8_t code; | ||
84 | uint16_t checksum; | ||
85 | uint32_t reserved; | ||
86 | }; | ||
87 | |||
88 | struct udp_packet | ||
89 | { | ||
90 | uint16_t source_port; | ||
91 | uint16_t dst_port; | ||
92 | uint16_t mlen_aka_reply_port_magic; | ||
93 | uint16_t checksum_aka_my_magic; | ||
94 | }; | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Structure of the data we tack on to the fake ICMP reply | ||
99 | * (last 4 bytes of the 64 bytes). | ||
100 | */ | ||
101 | struct extra_packet | ||
102 | { | ||
103 | /** | ||
104 | * if this is a reply to an icmp, what was the 'my_magic' | ||
105 | * value from the original icmp? | ||
106 | */ | ||
107 | uint16_t reply_port_magic; | ||
108 | |||
109 | /** | ||
110 | * magic value of the sender of this icmp message. | ||
111 | */ | ||
112 | uint16_t my_magic; | ||
113 | }; | ||
114 | |||
115 | static int udpsocks[NUM_UDP_PORTS]; | ||
116 | |||
117 | static uint16_t udpports[NUM_UDP_PORTS]; | ||
118 | |||
119 | static int icmpsock; | ||
120 | |||
121 | static struct in_addr dummy; | ||
122 | |||
123 | |||
124 | /** | ||
125 | * create a random port number that is not totally | ||
126 | * unlikely to be chosen by the nat box. | ||
127 | */ | ||
128 | static uint16_t make_port () | ||
129 | { | ||
130 | return 1024 + ( (unsigned int)rand ()) % (63 * 1024 - 2); | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * create a fresh udp socket bound to a random local port. | ||
136 | */ | ||
137 | static int | ||
138 | make_udp_socket (uint16_t *port) | ||
139 | { | ||
140 | int ret; | ||
141 | int tries; | ||
142 | struct sockaddr_in src; | ||
143 | |||
144 | for (tries=0;tries<MAX_TRIES;tries++) | ||
145 | { | ||
146 | ret = socket (AF_INET, SOCK_DGRAM, 0); | ||
147 | if (-1 == ret) | ||
148 | { | ||
149 | fprintf (stderr, | ||
150 | "Error opening udp socket: %s\n", | ||
151 | strerror (errno)); | ||
152 | return -1; | ||
153 | } | ||
154 | if (ret >= FD_SETSIZE) | ||
155 | { | ||
156 | fprintf (stderr, | ||
157 | "Socket number too large (%d > %u)\n", | ||
158 | ret, | ||
159 | (unsigned int) FD_SETSIZE); | ||
160 | close (ret); | ||
161 | return -1; | ||
162 | } | ||
163 | memset (&src, 0, sizeof (src)); | ||
164 | src.sin_family = AF_INET; | ||
165 | src.sin_port = htons (make_port ()); | ||
166 | if (0 != bind (ret, (struct sockaddr*) &src, sizeof (src))) | ||
167 | { | ||
168 | close (ret); | ||
169 | continue; | ||
170 | } | ||
171 | *port = ntohs (src.sin_port); | ||
172 | return ret; | ||
173 | } | ||
174 | fprintf (stderr, | ||
175 | "Error binding udp socket: %s\n", | ||
176 | strerror (errno)); | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | |||
181 | static uint16_t | ||
182 | calc_checksum(const uint16_t *data, | ||
183 | unsigned int bytes) | ||
184 | { | ||
185 | uint32_t sum; | ||
186 | unsigned int i; | ||
187 | |||
188 | sum = 0; | ||
189 | for (i=0;i<bytes/2;i++) | ||
190 | sum += data[i]; | ||
191 | sum = (sum & 0xffff) + (sum >> 16); | ||
192 | sum = htons(0xffff - sum); | ||
193 | return sum; | ||
194 | } | ||
195 | |||
196 | |||
197 | /** | ||
198 | * send an icmp message to the target. | ||
199 | * | ||
200 | * @param my_ip source address (our ip address) | ||
201 | * @param other target address | ||
202 | * @param target_port_number fake port number to put into icmp response | ||
203 | * as well as the icmpextradata as 'my_magic' | ||
204 | * @param source_port_number magic_number that enables the other peer to | ||
205 | * identify our port number ('reply in response to') to | ||
206 | * put in the data portion; 0 if we are initiating; | ||
207 | * goes into 'reply_port_magic' of the icmpextradata | ||
208 | */ | ||
209 | static void | ||
210 | send_icmp (const struct in_addr *my_ip, | ||
211 | const struct in_addr *other, | ||
212 | uint16_t target_port_number, | ||
213 | uint16_t source_port_number) | ||
214 | { | ||
215 | struct ip_packet ip_pkt; | ||
216 | struct icmp_packet icmp_pkt; | ||
217 | struct udp_packet udp_pkt; | ||
218 | struct sockaddr_in dst; | ||
219 | char packet[sizeof (ip_pkt) + sizeof (icmp_pkt) + sizeof (udp_pkt)]; | ||
220 | char sbuf [INET_ADDRSTRLEN]; | ||
221 | size_t off; | ||
222 | int err; | ||
223 | |||
224 | fprintf (stderr, | ||
225 | "Sending ICMP to `%s' at %u with reply magic %u\n", | ||
226 | inet_ntop (AF_INET, | ||
227 | other, | ||
228 | sbuf, | ||
229 | sizeof (sbuf)), | ||
230 | target_port_number, | ||
231 | source_port_number); | ||
232 | /* ip header: send to (known) ip address */ | ||
233 | off = 0; | ||
234 | memset(&ip_pkt, 0, sizeof(ip_pkt)); | ||
235 | ip_pkt.vers_ihl = 0x45;//|(pkt_len>>2);//5;//(ipversion << 4) | (iphdr_size >> 2); | ||
236 | ip_pkt.tos = 0; | ||
237 | ip_pkt.pkt_len = sizeof (packet); /* huh? */ | ||
238 | ip_pkt.id = 1; /* kernel will change anyway!? */ | ||
239 | ip_pkt.flags_frag_offset = 0; | ||
240 | ip_pkt.ttl = IPDEFTTL; | ||
241 | ip_pkt.proto = IPPROTO_ICMP; | ||
242 | ip_pkt.checksum = 0; /* maybe the kernel helps us out..? */ | ||
243 | ip_pkt.src_ip = my_ip->s_addr; | ||
244 | ip_pkt.dst_ip = other->s_addr; | ||
245 | ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt))); | ||
246 | memcpy (packet, &ip_pkt, sizeof (ip_pkt)); | ||
247 | off += sizeof (ip_pkt); | ||
248 | |||
249 | /* icmp reply: time exceeded */ | ||
250 | memset(&icmp_pkt, 0, sizeof(icmp_pkt)); | ||
251 | icmp_pkt.type = ICMP_TIME_EXCEEDED; | ||
252 | icmp_pkt.code = ICMP_NET_UNREACH; | ||
253 | icmp_pkt.reserved = 0; | ||
254 | icmp_pkt.checksum = 0; | ||
255 | icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&icmp_pkt, sizeof (icmp_pkt))); | ||
256 | memcpy (&packet[off], &icmp_pkt, sizeof (icmp_pkt)); | ||
257 | off += sizeof (icmp_pkt); | ||
258 | |||
259 | /* ip header of the presumably 'lost' udp packet */ | ||
260 | memset(&ip_pkt, 0, sizeof (ip_pkt)); | ||
261 | ip_pkt.vers_ihl = 0x45; | ||
262 | ip_pkt.tos = 0; | ||
263 | /* no idea why i need to shift the bits here, but not on ip_pkt->pkt_len... */ | ||
264 | ip_pkt.pkt_len = (sizeof (ip_pkt) + sizeof (icmp_pkt)) << 8; | ||
265 | ip_pkt.id = 1; /* kernel sets proper value htons(ip_id_counter); */ | ||
266 | ip_pkt.flags_frag_offset = 0; | ||
267 | ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */ | ||
268 | ip_pkt.proto = IPPROTO_UDP; | ||
269 | ip_pkt.src_ip = other->s_addr; | ||
270 | ip_pkt.dst_ip = dummy.s_addr; | ||
271 | ip_pkt.checksum = 0; | ||
272 | ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (ip_pkt))); | ||
273 | memcpy (&packet[off], &ip_pkt, sizeof (ip_pkt)); | ||
274 | off += sizeof (ip_pkt); | ||
275 | |||
276 | memset(&udp_pkt, 0, sizeof (udp_pkt)); | ||
277 | udp_pkt.source_port = htons (target_port_number); | ||
278 | udp_pkt.dst_port = htons (NAT_TRAV_PORT); | ||
279 | udp_pkt.mlen_aka_reply_port_magic = htons (source_port_number); | ||
280 | udp_pkt.checksum_aka_my_magic = htons (target_port_number); | ||
281 | memcpy (&packet[off], &udp_pkt, sizeof (udp_pkt)); | ||
282 | off += sizeof (udp_pkt); | ||
283 | |||
284 | memset (&dst, 0, sizeof (dst)); | ||
285 | dst.sin_family = AF_INET; | ||
286 | dst.sin_addr = *other; | ||
287 | err = sendto(icmpsock, | ||
288 | packet, | ||
289 | off, 0, | ||
290 | (struct sockaddr*)&dst, | ||
291 | sizeof(dst)); /* or sizeof 'struct sockaddr'? */ | ||
292 | if (err < 0) { | ||
293 | fprintf(stderr, | ||
294 | "sendto failed: %s\n", strerror(errno)); | ||
295 | } else if (err != off) | ||
296 | fprintf(stderr, | ||
297 | "Error: partial send of ICMP message\n"); | ||
298 | } | ||
299 | |||
300 | |||
301 | /** | ||
302 | * We discovered the IP address of the other peer. | ||
303 | * Try to connect back to it. | ||
304 | */ | ||
305 | static void | ||
306 | try_connect (const struct in_addr *my_ip, | ||
307 | const struct in_addr *other, | ||
308 | uint16_t port_magic) | ||
309 | { | ||
310 | unsigned int i; | ||
311 | |||
312 | for (i=0;i<NUM_UDP_PORTS;i++) | ||
313 | send_icmp (my_ip, other, make_port(), port_magic); | ||
314 | } | ||
315 | |||
316 | |||
317 | static void | ||
318 | process_icmp_response (const struct in_addr *my_ip, | ||
319 | int s) | ||
320 | { | ||
321 | char buf[65536]; | ||
322 | ssize_t have; | ||
323 | struct in_addr sip; | ||
324 | uint16_t my_magic; | ||
325 | uint16_t reply_magic; | ||
326 | struct ip_packet ip_pkt; | ||
327 | struct icmp_packet icmp_pkt; | ||
328 | struct udp_packet udp_pkt; | ||
329 | size_t off; | ||
330 | |||
331 | have = read (s, buf, sizeof (buf)); | ||
332 | if (have == -1) | ||
333 | { | ||
334 | fprintf (stderr, | ||
335 | "Error reading raw socket: %s\n", | ||
336 | strerror (errno)); | ||
337 | /* What now? */ | ||
338 | return; | ||
339 | } | ||
340 | if (have < sizeof (struct ip_packet) + sizeof (struct icmp_packet) + | ||
341 | sizeof (struct udp_packet)) | ||
342 | { | ||
343 | fprintf (stderr, | ||
344 | "Received invalid ICMP message: only %u bytes\n", | ||
345 | (unsigned int) have); | ||
346 | return; | ||
347 | } | ||
348 | off = 0; | ||
349 | memcpy (&ip_pkt, &buf[off], sizeof (ip_pkt)); | ||
350 | off += sizeof (ip_pkt); | ||
351 | memcpy (&icmp_pkt, &buf[off], sizeof (icmp_pkt)); | ||
352 | off += sizeof (icmp_pkt); | ||
353 | memcpy (&udp_pkt, &buf[off], sizeof (udp_pkt)); | ||
354 | off += sizeof (struct udp_packet); | ||
355 | |||
356 | /* If not ICMP and not TTL exceeded */ | ||
357 | if ( (ip_pkt.proto != IPPROTO_ICMP) || | ||
358 | (icmp_pkt.type != ICMP_TIME_EXCEEDED) || | ||
359 | (icmp_pkt.code != ICMP_NET_UNREACH) ) | ||
360 | { | ||
361 | fprintf (stderr, | ||
362 | "Received unexpected ICMP message contents, ignoring\n"); | ||
363 | return; | ||
364 | } | ||
365 | memcpy(&sip, &ip_pkt.src_ip, sizeof (sip)); | ||
366 | my_magic = ntohs (udp_pkt.checksum_aka_my_magic); | ||
367 | reply_magic = ntohs (udp_pkt.mlen_aka_reply_port_magic); | ||
368 | fprintf (stderr, | ||
369 | "Received ICMP from `%s' with hints %u and %u\n", | ||
370 | inet_ntop (AF_INET, | ||
371 | &sip, | ||
372 | buf, | ||
373 | sizeof (buf)), | ||
374 | my_magic, | ||
375 | reply_magic); | ||
376 | if (my_magic == 0) | ||
377 | { | ||
378 | try_connect (my_ip, &sip, reply_magic); | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | printf ("%s:%u\n", | ||
383 | inet_ntop (AF_INET, | ||
384 | &sip, | ||
385 | buf, | ||
386 | sizeof(buf)), | ||
387 | my_magic); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | |||
392 | static int | ||
393 | make_icmp_socket () | ||
394 | { | ||
395 | const int one = 1; | ||
396 | int ret; | ||
397 | |||
398 | ret = socket (AF_INET, SOCK_RAW, 0); | ||
399 | if (-1 == ret) | ||
400 | { | ||
401 | fprintf (stderr, | ||
402 | "Error opening RAW socket: %s\n", | ||
403 | strerror (errno)); | ||
404 | return -1; | ||
405 | } | ||
406 | if (ret >= FD_SETSIZE) | ||
407 | { | ||
408 | fprintf (stderr, | ||
409 | "Socket number too large (%d > %u)\n", | ||
410 | ret, | ||
411 | (unsigned int) FD_SETSIZE); | ||
412 | close (ret); | ||
413 | return -1; | ||
414 | } | ||
415 | if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST, | ||
416 | (char *)&one, sizeof(one)) == -1) | ||
417 | fprintf(stderr, | ||
418 | "setsockopt failed: %s\n", | ||
419 | strerror (errno)); | ||
420 | if (setsockopt(ret, IPPROTO_IP, IP_HDRINCL, | ||
421 | (char *)&one, sizeof(one)) == -1) | ||
422 | fprintf(stderr, | ||
423 | "setsockopt failed: %s\n", | ||
424 | strerror (errno)); | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | |||
429 | int | ||
430 | main (int argc, char *const *argv) | ||
431 | { | ||
432 | struct in_addr external; | ||
433 | unsigned int i; | ||
434 | unsigned int pos; | ||
435 | fd_set rs; | ||
436 | struct timeval tv; | ||
437 | struct sockaddr_in dst; | ||
438 | |||
439 | if (argc != 3) | ||
440 | { | ||
441 | fprintf (stderr, | ||
442 | "This program must be started with our external IP and the dummy IP address as arguments.\n"); | ||
443 | return 1; | ||
444 | } | ||
445 | if ( (1 != inet_pton (AF_INET, argv[1], &external)) || | ||
446 | (1 != inet_pton (AF_INET, argv[2], &dummy)) ) | ||
447 | { | ||
448 | fprintf (stderr, | ||
449 | "Error parsing IPv4 address: %s\n", | ||
450 | strerror (errno)); | ||
451 | return 1; | ||
452 | } | ||
453 | memset (&dst, 0, sizeof (dst)); | ||
454 | dst.sin_family = AF_INET; | ||
455 | dst.sin_port = htons (NAT_TRAV_PORT); | ||
456 | dst.sin_addr = dummy; | ||
457 | if (-1 == (icmpsock = make_icmp_socket())) | ||
458 | return 1; | ||
459 | for (i=0;i<NUM_UDP_PORTS;i++) | ||
460 | udpsocks[i] = make_udp_socket (&udpports[i]); | ||
461 | pos = 0; | ||
462 | while (1) | ||
463 | { | ||
464 | FD_ZERO (&rs); | ||
465 | FD_SET (icmpsock, &rs); | ||
466 | tv.tv_sec = 0; | ||
467 | tv.tv_usec = UDP_SEND_FREQUENCY_MS * 1000 * 1000; | ||
468 | select (icmpsock + 1, &rs, NULL, NULL, &tv); | ||
469 | /* FIXME: do I need my external IP here? */ | ||
470 | if (FD_ISSET (icmpsock, &rs)) | ||
471 | process_icmp_response (&external, icmpsock); | ||
472 | if (-1 == sendto (udpsocks[pos], | ||
473 | NULL, 0, 0, | ||
474 | (struct sockaddr*) &dst, sizeof (dst))) | ||
475 | { | ||
476 | fprintf (stderr, | ||
477 | "sendto failed: %s\n", | ||
478 | strerror (errno)); | ||
479 | close (udpsocks[pos]); | ||
480 | udpsocks[pos] = make_udp_socket (&udpports[pos]); | ||
481 | } | ||
482 | pos = (pos+1) % NUM_UDP_PORTS; | ||
483 | } | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | |||
488 | /* end of gnunet-nat-server.c */ | ||