2 This file is part of GNUnet.
3 (C) 2011-2014 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-transport.c
23 * @brief Tool to help configure, measure and control the transport subsystem.
24 * @author Christian Grothoff
25 * @author Nathan Evans
27 * This utility can be used to test if a transport mechanism for
28 * GNUnet is properly configured.
31 #include "gnunet_util_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_transport_service.h"
37 struct Iteration *next;
38 struct Iteration *prev;
39 struct GNUNET_TIME_Absolute start;
40 struct GNUNET_TIME_Absolute end;
42 struct GNUNET_TIME_Relative dur;
44 /* Transmission rate for this iteration in KB/s */
47 unsigned int msgs_sent;
52 * Timeout for a connections
54 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
57 * Benchmarking block size in bye
59 #define DEFAULT_MESSAGE_SIZE 1024
62 * Benchmarking message count
64 #define DEFAULT_MESSAGE_COUNT 1024
67 * Benchmarking iteration count
69 #define DEFAULT_ITERATION_COUNT 1
74 static int benchmark_send;
79 static int benchmark_receive;
84 static unsigned int benchmark_count;
89 static unsigned int benchmark_iterations;
94 static unsigned int benchmark_size;
99 static unsigned int benchmark_running;
102 * Which peer should we connect to?
107 * Handle to transport service.
109 static struct GNUNET_TRANSPORT_Handle *handle;
112 * Configuration handle
114 static struct GNUNET_CONFIGURATION_Handle *cfg;
119 struct GNUNET_TRANSPORT_TryConnectHandle *tc_handle;
122 struct Iteration *ihead;
123 struct Iteration *itail;
126 * Global return value (0 success).
130 * Handle for current transmission request.
132 static struct GNUNET_TRANSPORT_TransmitHandle *th;
134 struct GNUNET_TRANSPORT_Blacklist *bl_handle;
137 * Identity of the peer we transmit to / connect to.
138 * (equivalent to 'cpid' string).
140 static struct GNUNET_PeerIdentity pid;
143 * Task scheduled for cleanup / termination of the process.
145 static struct GNUNET_SCHEDULER_Task * end;
148 * Selected level of verbosity.
150 static int verbosity;
153 * Task run in monitor mode when the user presses CTRL-C to abort.
154 * Stops monitoring activity.
157 * @param tc scheduler context
160 shutdown_task (void *cls,
161 const struct GNUNET_SCHEDULER_TaskContext *tc)
163 struct Iteration *icur;
164 struct Iteration *inext;
166 unsigned int iterations;
168 unsigned long long avg_duration;
171 float stddev_duration;
173 if (NULL != tc_handle)
175 GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
180 GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
184 if (NULL != bl_handle )
186 GNUNET_TRANSPORT_blacklist_cancel (bl_handle);
192 GNUNET_TRANSPORT_disconnect (handle);
197 FPRINTF (stdout, "\n");
200 * All time values in ms
202 * #messages;#messagesize;#avg_dur;#avg_rate;#duration_i0;#duration_i0;... */
206 /* First iteration to calculcate avg and stddev */
212 while (NULL != (icur = inext))
215 icur->rate = ((benchmark_count * benchmark_size) / 1024) /
216 ((float) icur->dur.rel_value_us / (1000 * 1000));
218 FPRINTF (stdout, _("%llu B in %llu ms == %.2f KB/s!\n"),
219 ((long long unsigned int) benchmark_count * benchmark_size),
220 ((long long unsigned int) icur->dur.rel_value_us / 1000),
223 avg_duration += icur->dur.rel_value_us / (1000);
224 avg_rate += icur->rate;
228 /* Calculate average rate */
229 avg_rate /= iterations;
230 /* Calculate average duration */
231 avg_duration /= iterations;
236 while (NULL != (icur = inext))
239 stddev_rate += ((icur->rate-avg_rate) *
240 (icur->rate-avg_rate));
241 stddev_duration += (((icur->dur.rel_value_us / 1000) - avg_duration) *
242 ((icur->dur.rel_value_us / 1000) - avg_duration));
245 /* Calculate standard deviation rate */
246 stddev_rate = stddev_rate / iterations;
247 stddev_rate = sqrtf(stddev_rate);
249 /* Calculate standard deviation duration */
250 stddev_duration = stddev_duration / iterations;
251 stddev_duration = sqrtf(stddev_duration);
254 FPRINTF (stdout, _("%u;%u;%llu;%llu;%.2f;%.2f"),benchmark_count, benchmark_size,
255 avg_duration, (unsigned long long) stddev_duration, avg_rate, stddev_rate);
258 while (NULL != (icur = inext))
261 GNUNET_CONTAINER_DLL_remove (ihead, itail, icur);
263 FPRINTF (stdout, _(";%llu;%.2f"),
264 (long long unsigned int) (icur->dur.rel_value_us / 1000), icur->rate);
270 if (benchmark_receive)
272 duration = GNUNET_TIME_absolute_get_duration (start_time);
274 _("Received %llu bytes/s (%llu bytes in %s)\n"),
275 1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
277 GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
280 FPRINTF (stdout, _("\n"));
287 * Function called to notify a client about the socket
288 * begin ready to queue more data. @a buf will be
289 * NULL and @a size zero if the socket was closed for
290 * writing in the meantime.
293 * @param size number of bytes available in @a buf
294 * @param buf where the callee should write the message
295 * @return number of bytes written to @a buf
298 transmit_data (void *cls,
302 struct GNUNET_MessageHeader *m = buf;
305 if ((NULL == buf) || (0 == size))
313 GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
314 GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
315 m->size = ntohs (size);
316 m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
317 memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
319 if (itail->msgs_sent < benchmark_count)
321 th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, benchmark_size,
322 GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
331 if (itail->msgs_sent % 10 == 0 )
332 FPRINTF (stdout, _("."));
340 struct Iteration *icur;
345 benchmark_running = GNUNET_YES;
346 icur = GNUNET_new (struct Iteration);
347 GNUNET_CONTAINER_DLL_insert_tail (ihead, itail, icur);
348 icur->start = GNUNET_TIME_absolute_get();
352 _("\nStarting benchmark to `%s', starting to send %u messages in %u byte blocks\n"),
353 GNUNET_i2s (&pid), benchmark_count, benchmark_size);
355 th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, benchmark_size,
356 GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
366 static int it_count = 0;
369 itail->dur = GNUNET_TIME_absolute_get_duration (itail->start);
370 if (it_count == benchmark_iterations)
372 benchmark_running = GNUNET_NO;
374 GNUNET_SCHEDULER_cancel (end);
375 end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
387 * Function called to notify transport users that another
388 * peer connected to us.
391 * @param peer the peer that connected
394 notify_connect (void *cls,
395 const struct GNUNET_PeerIdentity *peer)
397 if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
400 _("Connected to different peer `%s'\n"), GNUNET_i2s (&pid));
406 _("Successfully connected to `%s'\n"),
409 if (NULL != tc_handle)
411 GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
420 * Function called to notify transport users that another
421 * peer disconnected from us.
424 * @param peer the peer that disconnected
427 notify_disconnect (void *cls,
428 const struct GNUNET_PeerIdentity *peer)
430 if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
432 if (GNUNET_YES == benchmark_running)
434 FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
441 * Function called by the transport for each received message.
444 * @param peer (claimed) identity of the other peer
445 * @param message the message
448 notify_receive (void *cls,
449 const struct GNUNET_PeerIdentity *peer,
450 const struct GNUNET_MessageHeader *message)
452 if (benchmark_receive)
454 if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
458 _("Received %u bytes from %s\n"),
459 (unsigned int) ntohs (message->size),
468 try_connect_cb (void *cls,
471 static int retries = 0;
473 if (GNUNET_OK == result)
483 FPRINTF (stdout, _("Retrying to connect to `%s'\n"), GNUNET_i2s (&pid));
485 tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
492 _("Failed to send connect request to transport service\n"));
494 GNUNET_SCHEDULER_cancel (end);
495 end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
502 blacklist_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
504 if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
508 _("Denying connection to `%s'\n"), GNUNET_i2s (peer));
509 return GNUNET_SYSERR;
518 * Function called with the result of the check if the 'transport'
519 * service is running.
521 * @param cls closure with our configuration
522 * @param result #GNUNET_YES if transport is running
525 testservice_task (void *cls, int result)
529 if (GNUNET_YES != result)
531 FPRINTF (stderr, _("Service `%s' is not running\n"), "transport");
535 if (GNUNET_SERVER_MAX_MESSAGE_SIZE <= benchmark_size)
537 FPRINTF (stderr, _("Message size too big!\n"));
543 FPRINTF (stderr, _("No peer identity given\n"));
546 if ((GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid, strlen (cpid),
549 FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
554 if (1 == benchmark_send)
558 _("Trying to send %u messages with size %u to peer `%s'\n"),
559 benchmark_count, benchmark_size, GNUNET_i2s (&pid));
561 else if (1 == benchmark_receive)
564 _("Trying to receive messages from peer `%s'\n"),
569 FPRINTF (stderr, _("No operation given\n"));
573 handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, ¬ify_receive,
574 ¬ify_connect, ¬ify_disconnect);
578 FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
583 bl_handle = GNUNET_TRANSPORT_blacklist (cfg, blacklist_cb, NULL);
584 tc_handle = GNUNET_TRANSPORT_try_connect(handle, &pid, try_connect_cb, NULL);
586 end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
593 * Main function that will be run by the scheduler.
596 * @param args remaining command-line arguments
597 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
598 * @param mycfg configuration
604 const struct GNUNET_CONFIGURATION_Handle *mycfg)
606 cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
607 GNUNET_CLIENT_service_test ("transport", cfg, GNUNET_TIME_UNIT_SECONDS,
608 &testservice_task, (void *) cfg);
612 main (int argc, char * const *argv)
615 benchmark_count = DEFAULT_MESSAGE_COUNT;
616 benchmark_size = DEFAULT_MESSAGE_SIZE;
617 benchmark_iterations = DEFAULT_ITERATION_COUNT;
618 benchmark_running = GNUNET_NO;
620 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
623 gettext_noop ("send data to peer"),
624 0, &GNUNET_GETOPT_set_one, &benchmark_send},
625 { 'r', "receive", NULL, gettext_noop
626 ("receive data from peer"), 0,
627 &GNUNET_GETOPT_set_one, &benchmark_receive},
628 { 'i', "iterations", NULL, gettext_noop
630 &GNUNET_GETOPT_set_uint, &benchmark_iterations},
631 { 'n', "number", NULL, gettext_noop
632 ("number of messages to send"), 1,
633 &GNUNET_GETOPT_set_uint, &benchmark_count},
634 { 'm', "messagesize", NULL, gettext_noop
635 ("message size to use"), 1,
636 &GNUNET_GETOPT_set_uint, &benchmark_size},
637 { 'p', "peer", "PEER",
638 gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
640 GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
641 GNUNET_GETOPT_OPTION_END
644 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
647 res = GNUNET_PROGRAM_run (argc, argv,
649 gettext_noop ("Direct access to transport service."),
652 GNUNET_free((void *) argv);
653 if (GNUNET_OK == res)
658 /* end of gnunet-transport.c */