ee977f5dc79aa236fa509de217e75bc16c6781cd
[oweals/gnunet.git] / src / transport / gnunet-transport.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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-transport.c
23  * @brief Tool to help configure, measure and control the transport subsystem.
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  *
27  * This utility can be used to test if a transport mechanism for
28  * GNUnet is properly configured.
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_resolver_service.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_transport_service.h"
36 #include "gnunet_nat_lib.h"
37
38 /**
39  * How long do we wait for the NAT test to report success?
40  */
41 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
42
43 /**
44  * Which peer should we connect to?
45  */
46 static char *cpid;
47
48 /**
49  * Handle to transport service.
50  */
51 static struct GNUNET_TRANSPORT_Handle *handle;
52
53 /**
54  * Option -s.
55  */
56 static int benchmark_send;
57
58 /**
59  * Option -b.
60  */
61 static int benchmark_receive;
62
63 /**
64  * Option -l.
65  */
66 static int benchmark_receive;
67
68 /**
69  * Option -i.
70  */
71 static int iterate_connections;
72
73 /**
74  * Option -t.
75  */
76 static int test_configuration;
77
78 /**
79  * Option -m.
80  */
81 static int monitor_connections;
82
83 /**
84  * Option -n.
85  */
86 static int numeric;
87
88 /**
89  * Global return value (0 success).
90  */
91 static int ret;
92
93 /**
94  * Number of bytes of traffic we received so far.
95  */
96 static unsigned long long traffic_received;
97
98 /**
99  * Number of bytes of traffic we sent so far.
100  */
101 static unsigned long long traffic_sent;
102
103 /**
104  * Starting time of transmitting/receiving data.
105  */
106 static struct GNUNET_TIME_Absolute start_time;
107
108 /**
109  * Handle for current transmission request.
110  */
111 static struct GNUNET_TRANSPORT_TransmitHandle *th;
112
113 /**
114  * Identity of the peer we transmit to / connect to.
115  * (equivalent to 'cpid' string).
116  */
117 static struct GNUNET_PeerIdentity pid;
118
119 /**
120  * Task scheduled for cleanup / termination of the process.
121  */
122 static GNUNET_SCHEDULER_TaskIdentifier end;
123
124 /**
125  * Selected level of verbosity.
126  */
127 static int verbosity;
128
129 /**
130  * Resolver process handle.
131  */
132 struct GNUNET_OS_Process *resolver;
133
134 /**
135  * Number of tasks running that still need the resolver.
136  */
137 static unsigned int resolver_users;
138
139
140 /**
141  * Context for a plugin test.
142  */
143 struct TestContext
144 {
145
146   /**
147    * Handle to the active NAT test.
148    */
149   struct GNUNET_NAT_Test *tst;
150
151   /**
152    * Task identifier for the timeout.
153    */
154   GNUNET_SCHEDULER_TaskIdentifier tsk;
155
156   /**
157    * Name of plugin under test.
158    */
159   const char *name;
160
161 };
162
163
164 /**
165  * Display the result of the test.
166  *
167  * @param tc test context
168  * @param result GNUNET_YES on success
169  */
170 static void
171 display_test_result (struct TestContext *tc, int result)
172 {
173   if (GNUNET_YES != result)
174   {
175     FPRINTF (stderr, "Configuration for plugin `%s' did not work!\n", tc->name);
176   }
177   else
178   {
179     FPRINTF (stderr, "Configuration for plugin `%s' is working!\n", tc->name);
180   }
181   if (GNUNET_SCHEDULER_NO_TASK != tc->tsk)
182   {
183     GNUNET_SCHEDULER_cancel (tc->tsk);
184     tc->tsk = GNUNET_SCHEDULER_NO_TASK;
185   }
186   if (NULL != tc->tst)
187   {
188     GNUNET_NAT_test_stop (tc->tst);
189     tc->tst = NULL;
190   }
191   GNUNET_free (tc);
192   resolver_users--;
193   if ((0 == resolver_users) && (NULL != resolver))
194   {
195     GNUNET_break (0 == GNUNET_OS_process_kill (resolver, SIGTERM));
196     GNUNET_OS_process_close (resolver);
197     resolver = NULL;
198   }
199 }
200
201
202 /**
203  * Function called by NAT on success.
204  * Clean up and update GUI (with success).
205  *
206  * @param cls test context
207  * @param success currently always GNUNET_OK
208  */
209 static void
210 result_callback (void *cls, int success)
211 {
212   struct TestContext *tc = cls;
213
214   display_test_result (tc, success);
215 }
216
217
218 /**
219  * Function called if NAT failed to confirm success.
220  * Clean up and update GUI (with failure).
221  *
222  * @param cls test context
223  * @param tc scheduler callback
224  */
225 static void
226 fail_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
227 {
228   struct TestContext *tstc = cls;
229
230   tstc->tsk = GNUNET_SCHEDULER_NO_TASK;
231   display_test_result (tstc, GNUNET_NO);
232 }
233
234
235 /**
236  * Test our plugin's configuration (NAT traversal, etc.).
237  *
238  * @param cfg configuration to test
239  */
240 static void
241 do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
242 {
243   char *plugins;
244   char *tok;
245   unsigned long long bnd_port;
246   unsigned long long adv_port;
247   struct TestContext *tc;
248
249   if (GNUNET_OK !=
250       GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
251                                              &plugins))
252   {
253     FPRINTF (stderr,
254              "%s",
255              _
256              ("No transport plugins configured, peer will never communicate\n"));
257     ret = 4;
258     return;
259   }
260   for (tok = strtok (plugins, " "); tok != NULL; tok = strtok (NULL, " "))
261   {
262     char section[12 + strlen (tok)];
263
264     GNUNET_snprintf (section, sizeof (section), "transport-%s", tok);
265     if (GNUNET_OK !=
266         GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT", &bnd_port))
267     {
268       FPRINTF (stderr,
269                _("No port configured for plugin `%s', cannot test it\n"), tok);
270       continue;
271     }
272     if (GNUNET_OK !=
273         GNUNET_CONFIGURATION_get_value_number (cfg, section, "ADVERTISED_PORT",
274                                                &adv_port))
275       adv_port = bnd_port;
276     if (NULL == resolver)
277       resolver =
278           GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-resolver",
279                                    "gnunet-service-resolver", NULL);
280     resolver_users++;
281     GNUNET_RESOLVER_connect (cfg);
282     tc = GNUNET_malloc (sizeof (struct TestContext));
283     tc->name = GNUNET_strdup (tok);
284     tc->tst =
285         GNUNET_NAT_test_start (cfg,
286                                (0 ==
287                                 strcasecmp (tok,
288                                             "udp")) ? GNUNET_NO : GNUNET_YES,
289                                (uint16_t) bnd_port, (uint16_t) adv_port,
290                                &result_callback, tc);
291     if (NULL == tc->tst)
292     {
293       display_test_result (tc, GNUNET_SYSERR);
294       continue;
295     }
296     tc->tsk = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, tc);
297   }
298   GNUNET_free (plugins);
299 }
300
301
302 /**
303  * Shutdown, print statistics.
304  */
305 static void
306 do_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
307 {
308   struct GNUNET_TIME_Relative duration;
309
310   if (NULL != th)
311   {
312     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
313     th = NULL;
314   }
315   GNUNET_TRANSPORT_disconnect (handle);
316   if (benchmark_receive)
317   {
318     duration = GNUNET_TIME_absolute_get_duration (start_time);
319     FPRINTF (stdout, _("Received %llu bytes/s (%llu bytes in %llu ms)\n"),
320              1000 * traffic_received / (1 + duration.rel_value),
321              traffic_received, (unsigned long long) duration.rel_value);
322   }
323   if (benchmark_send)
324   {
325     duration = GNUNET_TIME_absolute_get_duration (start_time);
326     FPRINTF (stdout, _("Transmitted %llu bytes/s (%llu bytes in %llu ms)\n"),
327              1000 * traffic_sent / (1 + duration.rel_value), traffic_sent,
328              (unsigned long long) duration.rel_value);
329   }
330 }
331
332
333 /**
334  * Function called to notify a client about the socket
335  * begin ready to queue more data.  "buf" will be
336  * NULL and "size" zero if the socket was closed for
337  * writing in the meantime.
338  *
339  * @param cls closure
340  * @param size number of bytes available in buf
341  * @param buf where the callee should write the message
342  * @return number of bytes written to buf
343  */
344 static size_t
345 transmit_data (void *cls, size_t size, void *buf)
346 {
347   struct GNUNET_MessageHeader *m = buf;
348
349   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
350   GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
351   m->size = ntohs (size);
352   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
353   memset (&m[1], 52, size - sizeof (struct GNUNET_MessageHeader));
354   traffic_sent += size;
355   th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, 32 * 1024, 0,
356                                                GNUNET_TIME_UNIT_FOREVER_REL,
357                                                &transmit_data, NULL);
358   if (verbosity > 0)
359     FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
360              GNUNET_i2s (&pid));
361   return size;
362 }
363
364
365 /**
366  * Function called to notify transport users that another
367  * peer connected to us.
368  *
369  * @param cls closure
370  * @param peer the peer that connected
371  * @param ats performance data
372  * @param ats_count number of entries in ats (excluding 0-termination)
373  */
374 static void
375 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
376                 const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
377 {
378   if (verbosity > 0)
379     FPRINTF (stdout, _("Connected to %s\n"), GNUNET_i2s (peer));
380   if (0 != memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity)))
381     return;
382   ret = 0;
383   if (benchmark_send)
384   {
385     start_time = GNUNET_TIME_absolute_get ();
386     th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer, 32 * 1024, 0,
387                                                  GNUNET_TIME_UNIT_FOREVER_REL,
388                                                  &transmit_data, NULL);
389   }
390   else
391   {
392     /* all done, terminate instantly */
393     GNUNET_SCHEDULER_cancel (end);
394     end = GNUNET_SCHEDULER_add_now (&do_disconnect, NULL);
395   }
396 }
397
398
399 /**
400  * Function called to notify transport users that another
401  * peer disconnected from us.
402  *
403  * @param cls closure
404  * @param peer the peer that disconnected
405  */
406 static void
407 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
408 {
409   if (verbosity > 0)
410     FPRINTF (stdout, _("Disconnected from %s\n"), GNUNET_i2s (peer));
411   if ((0 == memcmp (&pid, peer, sizeof (struct GNUNET_PeerIdentity))) &&
412       (NULL != th))
413   {
414     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
415     th = NULL;
416     GNUNET_SCHEDULER_cancel (end);
417     end = GNUNET_SCHEDULER_add_now (&do_disconnect, NULL);
418   }
419 }
420
421
422 /**
423  * Function called by the transport for each received message.
424  *
425  * @param cls closure
426  * @param peer (claimed) identity of the other peer
427  * @param message the message
428  * @param ats performance data
429  * @param ats_count number of entries in ats
430  */
431 static void
432 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
433                 const struct GNUNET_MessageHeader *message,
434                 const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
435 {
436   if (!benchmark_receive)
437     return;
438   if (verbosity > 0)
439     FPRINTF (stdout, _("Received %u bytes from %s\n"),
440              (unsigned int) ntohs (message->size), GNUNET_i2s (peer));
441   if (traffic_received == 0)
442     start_time = GNUNET_TIME_absolute_get ();
443   traffic_received += ntohs (message->size);
444 }
445
446 void
447 process_string (void *cls, const char *address)
448 {
449   struct GNUNET_HELLO_Address *addrcp = cls;
450
451   if ((address != NULL))
452   {
453     FPRINTF (stdout, _("Peer `%s': %s %s\n"), GNUNET_i2s (&addrcp->peer), addrcp->transport_name, address);
454   }
455   else
456   {
457     /* done */
458     GNUNET_free (addrcp);
459   }
460 }
461
462 /**
463  * Function to call with a binary address
464  *
465  * @param cls closure
466  * @param peer identity of the peer
467  * @param address binary address (NULL on disconnect)
468  */
469 static void
470 process_address (void *cls, const struct GNUNET_PeerIdentity *peer,
471                  const struct GNUNET_HELLO_Address *address)
472 {
473   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
474
475   if (peer == NULL)
476   {
477     /* done */
478     return;
479   }
480
481   if (address == NULL)
482   {
483     FPRINTF (stdout, _("Peer `%s' disconnected\n"), GNUNET_i2s (peer));
484     return;
485   }
486
487   /* Resolve address to string */
488   GNUNET_TRANSPORT_address_to_string (cfg, address, numeric,
489                                       GNUNET_TIME_UNIT_MINUTES, &process_string,
490                                       GNUNET_HELLO_address_copy(address));
491 }
492
493
494 /**
495  * Task run in monitor mode when the user presses CTRL-C to abort.
496  * Stops monitoring activity.
497  * 
498  * @param cls the 'struct GNUNET_TRANSPORT_PeerIterateContext *'
499  * @param tc scheduler context
500  */
501 static void
502 shutdown_task (void *cls,
503                const struct GNUNET_SCHEDULER_TaskContext *tc)
504 {
505   struct GNUNET_TRANSPORT_PeerIterateContext *pic = cls;
506
507   GNUNET_TRANSPORT_peer_get_active_addresses_cancel (pic);  
508 }
509
510
511
512 /**
513  * Main function that will be run by the scheduler.
514  *
515  * @param cls closure
516  * @param args remaining command-line arguments
517  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
518  * @param cfg configuration
519  */
520 static void
521 run (void *cls, char *const *args, const char *cfgfile,
522      const struct GNUNET_CONFIGURATION_Handle *cfg)
523 {
524   if (test_configuration)
525   {
526     do_test_configuration (cfg);
527   }
528   if (benchmark_send && (NULL == cpid))
529   {
530     FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
531              "-s", "-C");
532     return;
533   }
534   if (NULL != cpid)
535   {
536     ret = 1;
537     if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (cpid, &pid.hashPubKey))
538     {
539       FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
540       return;
541     }
542     handle =
543         GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
544                                   &notify_connect, &notify_disconnect);
545     GNUNET_TRANSPORT_try_connect (handle, &pid);
546     end =
547         GNUNET_SCHEDULER_add_delayed (benchmark_send ?
548                                       GNUNET_TIME_UNIT_FOREVER_REL :
549                                       GNUNET_TIME_UNIT_SECONDS, &do_disconnect,
550                                       NULL);
551   }
552   else if (benchmark_receive)
553   {
554     handle =
555         GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
556                                   &notify_connect, &notify_disconnect);
557     GNUNET_TRANSPORT_try_connect (handle, &pid);
558     end =
559         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
560                                       &do_disconnect, NULL);
561   }
562   if (iterate_connections)
563   {
564     GNUNET_TRANSPORT_peer_get_active_addresses (cfg, NULL, GNUNET_YES,
565                                                 GNUNET_TIME_UNIT_MINUTES,
566                                                 &process_address, (void *) cfg);
567   }
568   if (monitor_connections)
569   {
570     struct GNUNET_TRANSPORT_PeerIterateContext *pic;
571
572     pic = GNUNET_TRANSPORT_peer_get_active_addresses (cfg, NULL, GNUNET_NO,
573                                                       GNUNET_TIME_UNIT_FOREVER_REL,
574                                                       &process_address, (void *) cfg);
575     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
576                                   &shutdown_task,
577                                   pic);
578   }
579 }
580
581
582 int
583 main (int argc, char *const *argv)
584 {
585   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
586     {'b', "benchmark", NULL,
587      gettext_noop ("measure how fast we are receiving data (until CTRL-C)"),
588      0, &GNUNET_GETOPT_set_one, &benchmark_receive},
589     {'C', "connect", "PEER",
590      gettext_noop ("try to connect to the given peer"),
591      1, &GNUNET_GETOPT_set_string, &cpid},
592     {'i', "information", NULL,
593      gettext_noop ("provide information about all current connections (once)"),
594      0, &GNUNET_GETOPT_set_one, &iterate_connections},
595     {'m', "monitor", NULL,
596      gettext_noop ("provide information about all current connections (continuously)"),
597      0, &GNUNET_GETOPT_set_one, &monitor_connections},
598     {'n', "numeric", NULL,
599      gettext_noop ("do not resolve hostnames"),
600      0, &GNUNET_GETOPT_set_one, &numeric},
601     {'s', "send", NULL,
602      gettext_noop
603      ("send data for benchmarking to the other peer (until CTRL-C)"),
604      0, &GNUNET_GETOPT_set_one, &benchmark_send},
605     {'t', "test", NULL,
606      gettext_noop ("test transport configuration (involves external server)"),
607      0, &GNUNET_GETOPT_set_one, &test_configuration},
608     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
609     GNUNET_GETOPT_OPTION_END
610   };
611   return (GNUNET_OK ==
612           GNUNET_PROGRAM_run (argc, argv, "gnunet-transport",
613                               gettext_noop
614                               ("Direct access to transport service."), options,
615                               &run, NULL)) ? ret : 1;
616 }
617
618
619 /* end of gnunet-transport.c */