2 This file is part of GNUnet.
3 (C) 2011 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 nat/nat_test.c
23 * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server)
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_nat_lib.h"
39 struct NatActivity *next;
44 struct NatActivity *prev;
49 struct GNUNET_NETWORK_Handle *sock;
54 struct GNUNET_NAT_Test *h;
59 GNUNET_SCHEDULER_TaskIdentifier rtask;
63 * Handle to a NAT test.
65 struct GNUNET_NAT_Test
71 const struct GNUNET_CONFIGURATION_Handle *cfg;
76 GNUNET_NAT_TestCallback report;
86 struct GNUNET_NAT_Handle *nat;
91 struct GNUNET_NETWORK_Handle *lsock;
96 struct NatActivity *head;
101 struct NatActivity *tail;
106 GNUNET_SCHEDULER_TaskIdentifier ltask;
127 * Function called from GNUNET_NAT_register
128 * whenever someone asks us to do connection
131 * @param cls closure, our 'struct GNUNET_NAT_Handle'
132 * @param addr public IP address of the other peer
133 * @param addrlen actual lenght of the address
136 reversal_cb (void *cls,
137 const struct sockaddr *addr,
140 struct GNUNET_NAT_Test *h = cls;
141 const struct sockaddr_in *sa;
143 if (addrlen != sizeof (struct sockaddr_in))
145 sa = (const struct sockaddr_in *) addr;
146 if (h->data != sa->sin_port)
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Received connection reversal request for wrong port\n");
150 return; /* wrong port */
153 h->report (h->report_cls, GNUNET_OK);
158 * Activity on our incoming socket. Read data from the
159 * incoming connection.
161 * @param cls the 'struct NatActivity'
162 * @param tc scheduler context
166 const struct GNUNET_SCHEDULER_TaskContext *tc)
168 struct NatActivity *na = cls;
169 struct GNUNET_NAT_Test *tst;
171 na->rtask = GNUNET_SCHEDULER_NO_TASK;
173 GNUNET_CONTAINER_DLL_remove (tst->head,
178 // fimxe: read from socket...
180 GNUNET_NETWORK_socket_close (na->sock);
186 * Activity on our listen socket. Accept the
187 * incoming connection.
189 * @param cls the 'struct GNUNET_NAT_Test'
190 * @param tc scheduler context
193 do_accept (void *cls,
194 const struct GNUNET_SCHEDULER_TaskContext *tc)
196 struct GNUNET_NAT_Test *tst = cls;
197 struct GNUNET_NETWORK_Handle *s;
198 struct NatActivity *wl;
200 tst->ltask = GNUNET_SCHEDULER_NO_TASK;
201 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
203 tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
207 s = GNUNET_NETWORK_socket_accept (tst->lsock, NULL, NULL);
209 return; /* odd error */
210 wl = GNUNET_malloc (sizeof (struct NatActivity));
213 wl->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
217 GNUNET_CONTAINER_DLL_insert (tst->head,
224 * Address-callback, used to send message to gnunet-nat-server.
227 * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
228 * the previous (now invalid) one
229 * @param addr either the previous or the new public IP address
230 * @param addrlen actual lenght of the address
235 const struct sockaddr *addr,
238 struct GNUNET_NAT_Test *h = cls;
239 struct GNUNET_CLIENT_Connection *client;
240 struct GNUNET_NAT_TestMessage msg;
241 const struct sockaddr_in *sa;
243 if (GNUNET_YES != add_remove)
245 if (addrlen != sizeof (struct sockaddr_in))
246 return; /* ignore IPv6 here */
247 sa = (const struct sockaddr_in*) addr;
248 msg.header.size = htons (sizeof(struct GNUNET_NAT_TestMessage));
249 msg.header.type = htons (GNUNET_MESSAGE_TYPE_NAT_TEST);
250 msg.dst_ipv4 = sa->sin_addr.s_addr;
251 msg.dport = sa->sin_port;
253 msg.is_tcp = htonl ((uint32_t) h->is_tcp);
255 client = GNUNET_CLIENT_connect ("gnunet-nat-server",
257 GNUNET_break (GNUNET_OK ==
258 GNUNET_CLIENT_transmit_and_get_response (client,
260 GNUNET_TIME_UNIT_SECONDS,
263 GNUNET_CLIENT_disconnect (client, GNUNET_YES);
268 * Start testing if NAT traversal works using the
269 * given configuration (IPv4-only).
271 * @param cfg configuration for the NAT traversal
272 * @param is_tcp GNUNET_YES to test TCP, GNUNET_NO to test UDP
273 * @param bnd_port port to bind to, 0 for connection reversal
274 * @param adv_port externally advertised port to use
275 * @param report function to call with the result of the test
276 * @param report_cls closure for report
277 * @return handle to cancel NAT test
279 struct GNUNET_NAT_Test *
280 GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
284 GNUNET_NAT_TestCallback report,
287 struct GNUNET_NAT_Test *ret;
288 struct sockaddr_in sa;
289 const struct sockaddr *addrs[] = { (const struct sockaddr*) &sa };
290 const socklen_t addrlens[] = { sizeof (sa) };
292 memset (&sa, 0, sizeof (sa));
293 sa.sin_port = htons (bnd_port);
294 #if HAVE_SOCKADDR_IN_SIN_LEN
295 sa.sin_len = sizeof (sa);
298 ret = GNUNET_malloc (sizeof (struct GNUNET_NAT_Test));
300 ret->is_tcp = is_tcp;
301 ret->data = bnd_port;
302 ret->adv_port = adv_port;
303 ret->report = report;
304 ret->report_cls = report_cls;
307 ret->nat = GNUNET_NAT_register (cfg, is_tcp,
310 &addr_cb, &reversal_cb, ret);
314 ret->lsock = GNUNET_NETWORK_socket_create (AF_INET,
315 (is_tcp==GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM, 0);
316 if ( (ret->lsock == NULL) ||
317 (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret->lsock,
318 (const struct sockaddr*) &sa,
321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
322 _("Failed to create listen socket for NAT test\n"));
323 if (NULL != ret->lsock)
324 GNUNET_NETWORK_socket_close (ret->lsock);
328 GNUNET_break (GNUNET_OK ==
329 GNUNET_NETWORK_socket_listen (ret->lsock, 5));
330 ret->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
334 ret->nat = GNUNET_NAT_register (cfg, is_tcp,
337 &addr_cb, NULL, ret);
344 * Stop an active NAT test.
346 * @param tst test to stop.
349 GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst)
351 struct NatActivity *pos;
353 while (NULL != (pos = tst->head))
355 GNUNET_CONTAINER_DLL_remove (tst->head,
358 GNUNET_SCHEDULER_cancel (pos->rtask);
359 GNUNET_NETWORK_socket_close (pos->sock);
362 if (GNUNET_SCHEDULER_NO_TASK != tst->ltask)
363 GNUNET_SCHEDULER_cancel (tst->ltask);
364 if (NULL != tst->lsock)
365 GNUNET_NETWORK_socket_close (tst->lsock);
366 GNUNET_NAT_unregister (tst->nat);
370 /* end of nat_test.c */