2 This file is part of GNUnet.
3 Copyright (C) 2009--2013 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 ats-tool/gnunet-ats.c
23 * @brief ATS command line tool
24 * @author Matthias Wachs
27 #include "gnunet_util_lib.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet_transport_service.h"
32 * String to respresent unlimited
34 #define UNLIMITED_STRING "unlimited"
40 static int opt_resolve_addresses_numeric;
43 * CLI Opt: Print verbose ATS information
45 static int opt_verbose;
48 * CLI Option: List only addresses currently used (active)
50 static int opt_list_used;
53 * CLI Option: List all addresses
55 static int opt_list_all;
58 * CLI Option: set preference
60 static int opt_set_pref;
63 * CLI Option: print quotas configured
65 static int opt_print_quotas;
68 * CLI Option: Monitor addresses used
70 static int opt_monitor;
73 * CLI Option: use specific peer
75 static char *opt_pid_str;
78 * CLI Option: preference type to set
80 static char *opt_type_str;
83 * CLI Option: preference value to set
85 static unsigned int opt_pref_value;
93 * Number of results returned from service
95 static int stat_results;
98 * State: all pending receive operations done?
100 static int stat_receive_done;
103 * State: number of pending operations
105 static int stat_pending;
108 * ATS performance handle used
110 static struct GNUNET_ATS_PerformanceHandle *ph;
113 * ATS address list handle used
115 static struct GNUNET_ATS_AddressListHandle *alh;
118 * Configuration handle
120 static struct GNUNET_CONFIGURATION_Handle *cfg;
125 static struct GNUNET_SCHEDULER_Task *shutdown_task;
128 * Hashmap to store addresses
130 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
134 * Structure used to remember all pending address resolutions.
135 * We keep address information in here while we talk to transport
136 * to map the address to a string.
138 struct PendingResolutions
143 struct PendingResolutions *next;
148 struct PendingResolutions *prev;
151 * Copy of the address we are resolving.
153 struct GNUNET_HELLO_Address *address;
156 * Handle to the transport request to convert the address
159 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
164 struct GNUNET_ATS_Properties properties;
167 * Amount of outbound bandwidth assigned by ATS.
169 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
172 * Amount of inbound bandwidth assigned by ATS.
174 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
177 * Is this an active address?
184 * Information we keep for an address. Used to avoid
185 * printing the same data multiple times.
190 * Address information.
192 struct GNUNET_HELLO_Address *address;
195 * Current outbound bandwidth.
197 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
200 * Current inbound bandwidth.
202 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
205 * Is this an active address?
213 * Head of list of pending resolution requests.
215 static struct PendingResolutions *head;
218 * Tail of list of pending resolution requests.
220 static struct PendingResolutions *tail;
224 * Free address corresponding to a given peer.
227 * @param key peer identity
228 * @param value the `struct ATSAddress *` to be freed
229 * @return #GNUNET_YES (always)
232 free_addr_it (void *cls,
233 const struct GNUNET_PeerIdentity *key,
236 struct ATSAddress *a = value;
237 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (addresses, key, value));
238 GNUNET_HELLO_address_free (a->address);
245 * Task run on shutdown.
248 * @param tc scheduler context
252 const struct GNUNET_SCHEDULER_TaskContext *tc)
254 struct PendingResolutions * pr;
255 struct PendingResolutions * next;
256 unsigned int pending;
260 GNUNET_ATS_performance_list_addresses_cancel (alh);
266 GNUNET_ATS_performance_done (ph);
272 while (NULL != (pr = next))
275 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
276 GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
277 GNUNET_free(pr->address);
281 GNUNET_CONTAINER_multipeermap_iterate (addresses,
284 GNUNET_CONTAINER_multipeermap_destroy (addresses);
289 _("%u address resolutions had a timeout\n"),
291 if (opt_list_used || opt_list_all)
293 _("ATS returned stat_results for %u addresses\n"),
300 * Function to call with a textual representation of an address. This
301 * function will be called several times with different possible
302 * textual representations, and a last time with @a address being NULL
303 * to signal the end of the iteration. Note that @a address NULL
304 * always is the last call, regardless of the value in @a res.
306 * @param cls closure, a `struct PendingResolutions *`
307 * @param address NULL on end of iteration,
308 * otherwise 0-terminated printable UTF-8 string,
309 * in particular an empty string if @a res is #GNUNET_NO
310 * @param res result of the address to string conversion:
311 * if #GNUNET_OK: conversion successful
312 * if #GNUNET_NO: address was invalid (or not supported)
313 * if #GNUNET_SYSERR: communication error (IPC error)
316 transport_addr_to_str_cb (void *cls,
320 struct PendingResolutions *pr = cls;
325 GNUNET_CONTAINER_DLL_remove (head,
328 GNUNET_free (pr->address);
332 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
334 /* All messages received and no resolutions pending*/
335 if (shutdown_task != NULL)
336 GNUNET_SCHEDULER_cancel (shutdown_task);
337 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
345 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
346 GNUNET_i2s (&pr->address->peer),
347 pr->address->transport_name,
348 (unsigned int) pr->address->address_length);
352 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
353 GNUNET_i2s (&pr->address->peer),
354 pr->address->transport_name,
355 (unsigned int) pr->address->address_length);
358 /* continues below */
366 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
367 GNUNET_i2s (&pr->address->peer),
368 pr->address->transport_name,
370 GNUNET_ATS_print_network_type (pr->properties.scope),
371 ntohl (pr->bandwidth_out.value__),
372 ntohl (pr->bandwidth_in.value__),
373 pr->active ? _("active ") : _("inactive "));
378 * Closure for #find_address_it().
380 struct AddressFindCtx
383 * Address we are looking for.
385 const struct GNUNET_HELLO_Address *src;
388 * Where to write the `struct ATSAddress` if we found one that matches.
390 struct ATSAddress *res;
395 * Find address corresponding to a given peer.
397 * @param cls the `struct AddressFindCtx *`
398 * @param key peer identity
399 * @param value the `struct ATSAddress *` for an existing address
400 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
403 find_address_it (void *cls,
404 const struct GNUNET_PeerIdentity *key,
407 struct AddressFindCtx *actx = cls;
408 struct ATSAddress *exist = value;
410 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
421 * Signature of a function that is called with QoS information about an address.
423 * @param cls closure (NULL)
424 * @param address the address, NULL if ATS service was disconnected
425 * @param active #GNUNET_YES if this address is actively used
426 * to maintain a connection to a peer;
427 * #GNUNET_NO if the address is not actively used;
428 * #GNUNET_SYSERR if this address is no longer available for ATS
429 * @param bandwidth_out assigned outbound bandwidth for the connection
430 * @param bandwidth_in assigned inbound bandwidth for the connection
431 * @param prop performance data for the address (as far as known)
434 ats_perf_mon_cb (void *cls,
435 const struct GNUNET_HELLO_Address *address,
437 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
438 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
439 const struct GNUNET_ATS_Properties *prop)
441 struct PendingResolutions *pr;
442 struct PendingResolutions *cur;
443 struct PendingResolutions *next;
447 /* ATS service temporarily disconnected, remove current state */
449 for (cur = next; NULL != cur; cur = next)
452 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
453 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
454 GNUNET_HELLO_address_free (cur->address);
457 GNUNET_CONTAINER_multipeermap_iterate (addresses,
462 if (GNUNET_SYSERR == active)
465 struct AddressFindCtx actx;
469 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
473 if (NULL == actx.res)
478 GNUNET_break(GNUNET_OK ==
479 GNUNET_CONTAINER_multipeermap_remove (addresses,
483 _("Removed address of peer `%s' with plugin `%s'\n"),
484 GNUNET_i2s (&address->peer),
485 actx.res->address->transport_name);
486 GNUNET_HELLO_address_free (actx.res);
490 if (GNUNET_NO == opt_verbose)
492 struct AddressFindCtx actx;
493 struct ATSAddress *a;
497 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
501 if ((NULL != actx.res))
503 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
504 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
505 (active == actx.res->active))
507 return; /* Nothing to do here */
511 actx.res->bandwidth_in = bandwidth_in;
512 actx.res->bandwidth_out = bandwidth_out;
517 a = GNUNET_new (struct ATSAddress);
519 a->address = GNUNET_HELLO_address_copy(address);
520 a->bandwidth_in = bandwidth_in;
521 a->bandwidth_out = bandwidth_out;
523 GNUNET_CONTAINER_multipeermap_put (addresses,
526 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
530 pr = GNUNET_new (struct PendingResolutions);
531 pr->properties = *prop;
532 pr->address = GNUNET_HELLO_address_copy (address);
533 pr->bandwidth_in = bandwidth_in;
534 pr->bandwidth_out = bandwidth_out;
536 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
537 opt_resolve_addresses_numeric,
538 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
539 &transport_addr_to_str_cb,
541 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
548 * Signature of a function that is called with QoS information about an address.
550 * @param cls closure (NULL)
551 * @param address the address, NULL if ATS service was disconnected
552 * @param active is this address actively used to maintain a connection
554 * @param bandwidth_out assigned outbound bandwidth for the connection
555 * @param bandwidth_in assigned inbound bandwidth for the connection
556 * @param prop performance data for the address (as far as known)
559 ats_perf_cb (void *cls,
560 const struct GNUNET_HELLO_Address *address,
562 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
563 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
564 const struct GNUNET_ATS_Properties *prop)
566 struct PendingResolutions *pr;
570 /* All messages received */
571 stat_receive_done = GNUNET_YES;
573 if (0 == stat_pending)
575 /* All messages received and no resolutions pending*/
576 if (shutdown_task != NULL)
577 GNUNET_SCHEDULER_cancel (shutdown_task);
578 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
583 pr = GNUNET_new (struct PendingResolutions);
584 pr->properties = *prop;
585 pr->address = GNUNET_HELLO_address_copy (address);
586 pr->bandwidth_in = bandwidth_in;
587 pr->bandwidth_out = bandwidth_out;
589 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
590 opt_resolve_addresses_numeric,
591 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
592 &transport_addr_to_str_cb, pr);
593 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
600 * Print information about the quotas configured for the various
603 * @param cfg configuration to obtain quota information from
604 * @return total number of ATS network types known
607 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
609 char * entry_in = NULL;
610 char * entry_out = NULL;
611 char * quota_out_str;
613 unsigned long long int quota_out;
614 unsigned long long int quota_in;
617 for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
620 GNUNET_asprintf (&entry_out,
622 GNUNET_ATS_print_network_type (c));
623 GNUNET_asprintf (&entry_in,
625 GNUNET_ATS_print_network_type (c));
629 GNUNET_CONFIGURATION_get_value_string (cfg,
634 if (0 == strcmp (quota_out_str, UNLIMITED_STRING)
636 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
638 quota_out = UINT32_MAX;
640 GNUNET_free(quota_out_str);
641 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
646 "Outbound quota for network `%11s' not configured!\n",
647 GNUNET_ATS_print_network_type (c));
648 GNUNET_asprintf ("a_out_str, "-");
650 GNUNET_free(entry_out);
654 GNUNET_CONFIGURATION_get_value_string (cfg,
659 if (0 == strcmp (quota_in_str, UNLIMITED_STRING)
661 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
662 quota_in = UINT32_MAX;
663 GNUNET_free(quota_in_str);
664 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
669 "Inbound quota for network `%11s' not configured!\n",
670 GNUNET_ATS_print_network_type (c));
671 GNUNET_asprintf ("a_in_str, "-");
673 GNUNET_free(entry_in);
676 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
677 GNUNET_ATS_print_network_type (c),
680 GNUNET_free(quota_out_str);
681 GNUNET_free(quota_in_str);
683 return GNUNET_ATS_NetworkTypeCount;
688 * Function called with the result from the test if ATS is
689 * running. Runs the actual main logic.
691 * @param cls the `struct GNUNET_CONFIGURATION_Handle *`
692 * @param result result of the test, #GNUNET_YES if ATS is running
695 testservice_ats (void *cls,
698 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
699 struct GNUNET_PeerIdentity pid;
703 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
705 if (GNUNET_YES != result)
708 _("Service `%s' is not running\n"),
715 if (NULL != opt_pid_str)
718 != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_pid_str,
719 strlen (opt_pid_str), &pid.public_key))
722 _("Failed to parse peer identity `%s'\n"),
728 c = opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
732 _("Please select one operation : %s or %s or %s or %s or %s\n"),
741 opt_list_used = GNUNET_YES; /* set default */
742 if (opt_print_quotas)
744 ret = print_quotas (cfg);
749 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
754 _("Cannot connect to ATS service, exiting...\n"));
758 alh = GNUNET_ATS_performance_list_addresses (ph,
759 (NULL == opt_pid_str) ? NULL : &pid,
766 _("Cannot issue request to ATS service, exiting...\n"));
767 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
770 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
774 else if (opt_list_used)
776 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
780 _("Cannot connect to ATS service, exiting...\n"));
782 alh = GNUNET_ATS_performance_list_addresses (ph,
783 (NULL == opt_pid_str)
792 _("Cannot issue request to ATS service, exiting...\n"));
793 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
796 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
800 else if (opt_monitor)
802 ph = GNUNET_ATS_performance_init (cfg,
808 _("Cannot connect to ATS service, exiting...\n"));
809 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
814 else if (opt_set_pref)
816 if (NULL == opt_type_str)
820 _("No preference type given!\n"));
823 if (NULL == opt_pid_str)
827 _("No peer given!\n"));
831 for (c = 0; c < strlen (opt_type_str); c++)
833 if (isupper (opt_type_str[c]))
834 opt_type_str[c] = tolower (opt_type_str[c]);
837 if (0 == strcasecmp ("latency", opt_type_str))
838 type = GNUNET_ATS_PREFERENCE_LATENCY;
839 else if (0 == strcasecmp ("bandwidth", opt_type_str))
840 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
845 _("Valid type required\n"));
850 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
854 _("Cannot connect to ATS service, exiting...\n"));
856 GNUNET_ATS_performance_change_preference (ph,
859 (double) opt_pref_value,
860 GNUNET_ATS_PREFERENCE_END);
862 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
871 * Main function that will be run by the scheduler.
874 * @param args remaining command-line arguments
875 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
876 * @param my_cfg configuration
882 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
884 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
885 GNUNET_CLIENT_service_test ("ats", cfg,
886 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
887 &testservice_ats, (void *) cfg);
894 * @param argc number of arguments from the command line
895 * @param argv command line arguments
896 * @return 0 ok, 1 on error
904 opt_resolve_addresses_numeric = GNUNET_NO;
905 opt_monitor = GNUNET_NO;
906 opt_list_all = GNUNET_NO;
907 opt_list_used = GNUNET_NO;
908 opt_set_pref = GNUNET_NO;
910 stat_receive_done = GNUNET_NO;
913 static const struct GNUNET_GETOPT_CommandLineOption options[] =
916 gettext_noop ("get list of active addresses currently used"), 0,
917 &GNUNET_GETOPT_set_one, &opt_list_used },
918 { 'a', "all", NULL, gettext_noop ("get list of all active addresses"), 0,
919 &GNUNET_GETOPT_set_one, &opt_list_all },
920 { 'n', "numeric", NULL,
921 gettext_noop ("do not resolve IP addresses to hostnames"), 0,
922 &GNUNET_GETOPT_set_one, &opt_resolve_addresses_numeric },
923 { 'm', "monitor", NULL, gettext_noop ("monitor mode"), 0,
924 &GNUNET_GETOPT_set_one, &opt_monitor },
925 { 'p', "preference", NULL, gettext_noop ("set preference for the given peer"),
926 0, &GNUNET_GETOPT_set_one, &opt_set_pref },
927 { 'q', "quotas", NULL, gettext_noop ("print all configured quotas"), 0,
928 &GNUNET_GETOPT_set_one, &opt_print_quotas },
929 { 'i', "id", "TYPE", gettext_noop ("peer id"), 1, &GNUNET_GETOPT_set_string,
931 { 't', "type", "TYPE",
932 gettext_noop ("preference type to set: latency | bandwidth"), 1,
933 &GNUNET_GETOPT_set_string, &opt_type_str },
934 { 'k', "value", "VALUE", gettext_noop ("preference value"), 1,
935 &GNUNET_GETOPT_set_uint, &opt_pref_value },
936 { 'V', "verbose", NULL,
937 gettext_noop ("verbose output (include ATS address properties)"), 0,
938 &GNUNET_GETOPT_set_one, &opt_verbose }, GNUNET_GETOPT_OPTION_END };
940 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
943 res = GNUNET_PROGRAM_run (argc, argv, "gnunet-ats",
944 gettext_noop ("Print information about ATS state"),
947 GNUNET_free_non_null(opt_pid_str);
948 GNUNET_free_non_null(opt_type_str);
949 GNUNET_free((void *) argv);
951 if (GNUNET_OK == res)
958 /* end of gnunet-ats.c */