2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file src/transport/gnunet-nat-client-udp.c
23 * @brief Test for NAT traversal using ICMP method.
24 * @author Christian Grothoff
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <sys/types.h>
36 #include <netinet/in.h>
40 * How often do we send our UDP messages to keep ports open (and to
41 * try to connect, of course). Use small value since we are the
42 * initiator and should hence be rather aggressive.
44 #define UDP_SEND_FREQUENCY_MS 5
47 * Port we always try to use.
49 #define NAT_TRAV_PORT 22223
52 * Number of UDP ports to keep open at the same time (typically >= 256).
53 * Should be less than FD_SETSIZE.
55 #define NUM_UDP_PORTS 1000
58 * How often do we retry to open and bind a UDP socket before giving up?
60 #define MAX_BIND_TRIES 10
63 * How often do we try at most? We expect to need (for the worst kind
64 * of NAT) on average 64512 / 512 = 126 attempts to have the right
65 * destination port and we then need to also (in the worst case) have
66 * the right source port (so 126 * 64512 = 8128512 packets on
67 * average!). That's obviously a bit much, so we give up earlier. The
68 * given value corresponds to about 1 minute of runtime (for a send
69 * frequency of one packet per ms).
71 * NOW: if the *server* would listen for Linux-generated ICMP
72 * "Destination unreachables" we *might* increase our chances since
73 * maybe the firewall has some older/other UDP rules (this was
74 * the case during testing for me), but obviously that would mean
75 * more SUID'ed code. Yuck.
77 #define MAX_TRIES 62500
79 #define LOW_PORT 32768
82 * create a random port number that is not totally
83 * unlikely to be chosen by the nat box.
88 return LOW_PORT + ( (unsigned int)rand ()) % (64 * 1024 - LOW_PORT);
93 * create a fresh udp socket bound to a random local port,
94 * or, if the argument is zero, to the NAT_TRAV_PORT.
100 make_udp_socket (int i)
104 struct sockaddr_in src;
106 for (tries=0;tries<MAX_BIND_TRIES;tries++)
108 ret = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
112 "Error opening udp socket: %s\n",
116 if (ret >= FD_SETSIZE)
119 "Socket number too large (%d > %u)\n",
121 (unsigned int) FD_SETSIZE);
125 memset (&src, 0, sizeof (src));
126 src.sin_family = AF_INET;
128 src.sin_port = htons (NAT_TRAV_PORT);
130 src.sin_port = htons (make_port ());
131 if (0 != bind (ret, (struct sockaddr*) &src, sizeof (src)))
139 "Error binding udp socket: %s\n",
148 main (int argc, char *const *argv)
150 int udpsocks[NUM_UDP_PORTS];
152 struct in_addr external;
153 struct in_addr target;
158 struct sockaddr_in dst;
159 struct sockaddr_in src;
161 char dummybuf[65536];
170 "This program must be started with our IP and the targets external IP as arguments.\n");
173 if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
174 (1 != inet_pton (AF_INET, argv[2], &target)) )
177 "Error parsing IPv4 address: %s\n",
183 "gnunet-nat-client %s %s",
186 if (0 != (ret = system (command)))
190 "Error running `%s': %s\n",
196 "Trying to connect to `%s'\n",
199 for (i=0;i<NUM_UDP_PORTS;i++)
200 udpsocks[i] = make_udp_socket (i);
201 memset (&dst, 0, sizeof (dst));
202 dst.sin_family = AF_INET;
203 dst.sin_addr = target;
206 while (MAX_TRIES > tries++)
209 for (i=0;i<NUM_UDP_PORTS;i++)
211 if (udpsocks[i] != -1)
212 FD_SET (udpsocks[i], &rs);
213 if (udpsocks[i] > max)
217 tv.tv_usec = UDP_SEND_FREQUENCY_MS * 1000;
218 select (max + 1, &rs, NULL, NULL, &tv);
219 for (i=0;i<NUM_UDP_PORTS;i++)
221 if (udpsocks[i] == -1)
223 if (! FD_ISSET (udpsocks[i], &rs))
226 recvfrom (udpsocks[i],
227 dummybuf, sizeof (dummybuf), 0,
228 (struct sockaddr*) &src,
230 if (slen != sizeof (src))
233 "Unexpected size of address.\n");
236 if (0 != memcmp (&src.sin_addr,
241 "Unexpected sender IP\n");
244 /* discovered port! */
248 ntohs (src.sin_port));
249 dst.sin_port = src.sin_port;
250 if (-1 == sendto (udpsocks[i],
252 (struct sockaddr*) &dst, sizeof (dst)))
255 "sendto failed: %s\n",
261 "Succeeded after %u packets.\n",
265 if (udpsocks[pos] == -1)
267 udpsocks[pos] = make_udp_socket (pos);
270 if ( (0 == ((unsigned int)rand() % NUM_UDP_PORTS)) ||
272 dst.sin_port = htons (NAT_TRAV_PORT);
274 dst.sin_port = htons (make_port ());
276 "Sending UDP packet to `%s:%u'\n",
278 ntohs (dst.sin_port));
280 if (-1 == sendto (udpsocks[pos],
282 (struct sockaddr*) &dst, sizeof (dst)))
285 "sendto failed: %s\n",
287 close (udpsocks[pos]);
288 udpsocks[pos] = make_udp_socket (pos);
290 pos = (pos+1) % NUM_UDP_PORTS;
293 "Giving up after %u tries.\n",
298 /* end of client-test.c */