2 This file is part of GNUnet.
3 Copyright (C) 2009--2015 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file ats-tool/gnunet-ats.c
23 * @brief ATS command line tool
24 * @author Matthias Wachs
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet_ats_service.h"
30 #include "gnunet_transport_service.h"
33 * String to respresent unlimited
35 #define UNLIMITED_STRING "unlimited"
41 static int opt_resolve_addresses_numeric;
44 * CLI Opt: Print verbose ATS information
46 static int opt_verbose;
49 * CLI Option: List only addresses currently used (active)
51 static int opt_list_used;
54 * CLI Option: List all addresses
56 static int opt_list_all;
59 * CLI Option: set preference
61 static int opt_set_pref;
64 * CLI Option: print quotas configured
66 static int opt_print_quotas;
69 * CLI Option: Monitor addresses used
71 static int opt_monitor;
74 * CLI Option: use specific peer
76 static char *opt_pid_str;
79 * CLI Option: preference type to set
81 static char *opt_type_str;
84 * CLI Option: preference value to set
86 static unsigned int opt_pref_value;
94 * Number of results returned from service
96 static int stat_results;
99 * State: all pending receive operations done?
101 static int stat_receive_done;
104 * State: number of pending operations
106 static int stat_pending;
109 * Which peer should we connect to?
111 static char *cpid_str;
114 * ATS performance handle used
116 static struct GNUNET_ATS_PerformanceHandle *ph;
119 * Our connectivity handle.
121 static struct GNUNET_ATS_ConnectivityHandle *ats_ch;
124 * Handle for address suggestion request.
126 static struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
129 * ATS address list handle used
131 static struct GNUNET_ATS_AddressListHandle *alh;
134 * Configuration handle
136 static struct GNUNET_CONFIGURATION_Handle *cfg;
141 static struct GNUNET_SCHEDULER_Task *shutdown_task;
144 * Hashmap to store addresses
146 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
150 * Structure used to remember all pending address resolutions.
151 * We keep address information in here while we talk to transport
152 * to map the address to a string.
154 struct PendingResolutions
159 struct PendingResolutions *next;
164 struct PendingResolutions *prev;
167 * Copy of the address we are resolving.
169 struct GNUNET_HELLO_Address *address;
172 * Handle to the transport request to convert the address
175 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
180 struct GNUNET_ATS_Properties properties;
183 * Amount of outbound bandwidth assigned by ATS.
185 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
188 * Amount of inbound bandwidth assigned by ATS.
190 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
193 * Is this an active address?
200 * Information we keep for an address. Used to avoid
201 * printing the same data multiple times.
206 * Address information.
208 struct GNUNET_HELLO_Address *address;
211 * Current outbound bandwidth.
213 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
216 * Current inbound bandwidth.
218 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
221 * Is this an active address?
229 * Head of list of pending resolution requests.
231 static struct PendingResolutions *head;
234 * Tail of list of pending resolution requests.
236 static struct PendingResolutions *tail;
240 * Free address corresponding to a given peer.
243 * @param key peer identity
244 * @param value the `struct ATSAddress *` to be freed
245 * @return #GNUNET_YES (always)
248 free_addr_it (void *cls,
249 const struct GNUNET_PeerIdentity *key,
252 struct ATSAddress *a = value;
254 GNUNET_assert (GNUNET_OK ==
255 GNUNET_CONTAINER_multipeermap_remove (addresses,
258 GNUNET_HELLO_address_free (a->address);
265 * Task run on shutdown.
268 * @param tc scheduler context
272 const struct GNUNET_SCHEDULER_TaskContext *tc)
274 struct PendingResolutions * pr;
275 struct PendingResolutions * next;
276 unsigned int pending;
280 GNUNET_ATS_performance_list_addresses_cancel (alh);
286 GNUNET_ATS_performance_done (ph);
292 while (NULL != (pr = next))
295 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
296 GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
297 GNUNET_free(pr->address);
301 GNUNET_CONTAINER_multipeermap_iterate (addresses,
304 GNUNET_CONTAINER_multipeermap_destroy (addresses);
309 _("%u address resolutions had a timeout\n"),
311 if (opt_list_used || opt_list_all)
313 _("ATS returned stat_results for %u addresses\n"),
318 GNUNET_ATS_connectivity_suggest_cancel (ats_sh);
323 GNUNET_ATS_connectivity_done (ats_ch);
331 * Function to call with a textual representation of an address. This
332 * function will be called several times with different possible
333 * textual representations, and a last time with @a address being NULL
334 * to signal the end of the iteration. Note that @a address NULL
335 * always is the last call, regardless of the value in @a res.
337 * @param cls closure, a `struct PendingResolutions *`
338 * @param address NULL on end of iteration,
339 * otherwise 0-terminated printable UTF-8 string,
340 * in particular an empty string if @a res is #GNUNET_NO
341 * @param res result of the address to string conversion:
342 * if #GNUNET_OK: conversion successful
343 * if #GNUNET_NO: address was invalid (or not supported)
344 * if #GNUNET_SYSERR: communication error (IPC error)
347 transport_addr_to_str_cb (void *cls,
351 struct PendingResolutions *pr = cls;
356 GNUNET_CONTAINER_DLL_remove (head,
359 GNUNET_free (pr->address);
363 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
365 /* All messages received and no resolutions pending*/
366 if (shutdown_task != NULL)
367 GNUNET_SCHEDULER_cancel (shutdown_task);
368 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
376 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
377 GNUNET_i2s (&pr->address->peer),
378 pr->address->transport_name,
379 (unsigned int) pr->address->address_length);
383 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
384 GNUNET_i2s (&pr->address->peer),
385 pr->address->transport_name,
386 (unsigned int) pr->address->address_length);
389 /* continues below */
397 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
398 GNUNET_i2s (&pr->address->peer),
399 pr->address->transport_name,
401 GNUNET_ATS_print_network_type (pr->properties.scope),
402 ntohl (pr->bandwidth_out.value__),
403 ntohl (pr->bandwidth_in.value__),
404 pr->active ? _("active ") : _("inactive "));
409 * Closure for #find_address_it().
411 struct AddressFindCtx
414 * Address we are looking for.
416 const struct GNUNET_HELLO_Address *src;
419 * Where to write the `struct ATSAddress` if we found one that matches.
421 struct ATSAddress *res;
426 * Find address corresponding to a given peer.
428 * @param cls the `struct AddressFindCtx *`
429 * @param key peer identity
430 * @param value the `struct ATSAddress *` for an existing address
431 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
434 find_address_it (void *cls,
435 const struct GNUNET_PeerIdentity *key,
438 struct AddressFindCtx *actx = cls;
439 struct ATSAddress *exist = value;
441 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
452 * Signature of a function that is called with QoS information about an address.
454 * @param cls closure (NULL)
455 * @param address the address, NULL if ATS service was disconnected
456 * @param active #GNUNET_YES if this address is actively used
457 * to maintain a connection to a peer;
458 * #GNUNET_NO if the address is not actively used;
459 * #GNUNET_SYSERR if this address is no longer available for ATS
460 * @param bandwidth_out assigned outbound bandwidth for the connection
461 * @param bandwidth_in assigned inbound bandwidth for the connection
462 * @param prop performance data for the address (as far as known)
465 ats_perf_mon_cb (void *cls,
466 const struct GNUNET_HELLO_Address *address,
468 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
469 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
470 const struct GNUNET_ATS_Properties *prop)
472 struct PendingResolutions *pr;
473 struct PendingResolutions *cur;
474 struct PendingResolutions *next;
478 /* ATS service temporarily disconnected, remove current state */
480 for (cur = next; NULL != cur; cur = next)
483 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
484 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
485 GNUNET_HELLO_address_free (cur->address);
488 GNUNET_CONTAINER_multipeermap_iterate (addresses,
493 if (GNUNET_SYSERR == active)
496 struct AddressFindCtx actx;
500 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
504 if (NULL == actx.res)
509 GNUNET_break(GNUNET_OK ==
510 GNUNET_CONTAINER_multipeermap_remove (addresses,
514 _("Removed address of peer `%s' with plugin `%s'\n"),
515 GNUNET_i2s (&address->peer),
516 actx.res->address->transport_name);
517 GNUNET_HELLO_address_free (actx.res);
521 if (GNUNET_NO == opt_verbose)
523 struct AddressFindCtx actx;
524 struct ATSAddress *a;
528 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
532 if ((NULL != actx.res))
534 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
535 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
536 (active == actx.res->active))
538 return; /* Nothing to do here */
542 actx.res->bandwidth_in = bandwidth_in;
543 actx.res->bandwidth_out = bandwidth_out;
548 a = GNUNET_new (struct ATSAddress);
550 a->address = GNUNET_HELLO_address_copy(address);
551 a->bandwidth_in = bandwidth_in;
552 a->bandwidth_out = bandwidth_out;
554 GNUNET_CONTAINER_multipeermap_put (addresses,
557 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
561 pr = GNUNET_new (struct PendingResolutions);
562 pr->properties = *prop;
563 pr->address = GNUNET_HELLO_address_copy (address);
564 pr->bandwidth_in = bandwidth_in;
565 pr->bandwidth_out = bandwidth_out;
567 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
568 opt_resolve_addresses_numeric,
569 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
570 &transport_addr_to_str_cb,
572 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
579 * Signature of a function that is called with QoS information about an address.
581 * @param cls closure (NULL)
582 * @param address the address, NULL if ATS service was disconnected
583 * @param active is this address actively used to maintain a connection
585 * @param bandwidth_out assigned outbound bandwidth for the connection
586 * @param bandwidth_in assigned inbound bandwidth for the connection
587 * @param prop performance data for the address (as far as known)
590 ats_perf_cb (void *cls,
591 const struct GNUNET_HELLO_Address *address,
593 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
594 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
595 const struct GNUNET_ATS_Properties *prop)
597 struct PendingResolutions *pr;
601 /* All messages received */
602 stat_receive_done = GNUNET_YES;
604 if (0 == stat_pending)
606 /* All messages received and no resolutions pending*/
607 if (shutdown_task != NULL)
608 GNUNET_SCHEDULER_cancel (shutdown_task);
609 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
614 pr = GNUNET_new (struct PendingResolutions);
615 pr->properties = *prop;
616 pr->address = GNUNET_HELLO_address_copy (address);
617 pr->bandwidth_in = bandwidth_in;
618 pr->bandwidth_out = bandwidth_out;
620 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
621 opt_resolve_addresses_numeric,
622 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
623 &transport_addr_to_str_cb, pr);
624 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
631 * Print information about the quotas configured for the various
634 * @param cfg configuration to obtain quota information from
635 * @return total number of ATS network types known
638 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
640 char * entry_in = NULL;
641 char * entry_out = NULL;
642 char * quota_out_str;
644 unsigned long long int quota_out;
645 unsigned long long int quota_in;
648 for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
651 GNUNET_asprintf (&entry_out,
653 GNUNET_ATS_print_network_type (c));
654 GNUNET_asprintf (&entry_in,
656 GNUNET_ATS_print_network_type (c));
660 GNUNET_CONFIGURATION_get_value_string (cfg,
665 if (0 == strcmp (quota_out_str, UNLIMITED_STRING)
667 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
669 quota_out = UINT32_MAX;
671 GNUNET_free(quota_out_str);
672 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
677 "Outbound quota for network `%11s' not configured!\n",
678 GNUNET_ATS_print_network_type (c));
679 GNUNET_asprintf ("a_out_str, "-");
681 GNUNET_free (entry_out);
685 GNUNET_CONFIGURATION_get_value_string (cfg,
690 if (0 == strcmp (quota_in_str, UNLIMITED_STRING)
692 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
693 quota_in = UINT32_MAX;
694 GNUNET_free (quota_in_str);
695 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
700 "Inbound quota for network `%11s' not configured!\n",
701 GNUNET_ATS_print_network_type (c));
702 GNUNET_asprintf ("a_in_str, "-");
704 GNUNET_free(entry_in);
707 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
708 GNUNET_ATS_print_network_type (c),
711 GNUNET_free(quota_out_str);
712 GNUNET_free(quota_in_str);
714 return GNUNET_ATS_NetworkTypeCount;
719 * Main function that will be run by the scheduler.
722 * @param args remaining command-line arguments
723 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
724 * @param my_cfg configuration
730 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
732 struct GNUNET_PeerIdentity pid;
733 struct GNUNET_PeerIdentity cpid;
737 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
738 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
742 if (NULL != opt_pid_str)
745 GNUNET_CRYPTO_eddsa_public_key_from_string (opt_pid_str,
746 strlen (opt_pid_str),
750 _("Failed to parse peer identity `%s'\n"),
755 if (NULL != cpid_str)
758 GNUNET_CRYPTO_eddsa_public_key_from_string (cpid_str,
763 _("Failed to parse peer identity `%s'\n"),
770 c += opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
775 _("Please select one operation: %s or %s or %s or %s or %s\n"),
784 opt_list_used = GNUNET_YES; /* set default */
785 if (opt_print_quotas)
787 ret = print_quotas (cfg);
792 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
797 _("Cannot connect to ATS service, exiting...\n"));
800 alh = GNUNET_ATS_performance_list_addresses (ph,
801 (NULL == opt_pid_str) ? NULL : &pid,
808 _("Cannot issue request to ATS service, exiting...\n"));
809 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
812 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
819 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
823 _("Cannot connect to ATS service, exiting...\n"));
825 alh = GNUNET_ATS_performance_list_addresses (ph,
826 (NULL == opt_pid_str)
835 _("Cannot issue request to ATS service, exiting...\n"));
836 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
839 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
846 ph = GNUNET_ATS_performance_init (cfg,
852 _("Cannot connect to ATS service, exiting...\n"));
853 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
860 if (NULL == opt_type_str)
864 _("No preference type given!\n"));
867 if (NULL == opt_pid_str)
871 _("No peer given!\n"));
875 for (c = 0; c < strlen (opt_type_str); c++)
877 if (isupper (opt_type_str[c]))
878 opt_type_str[c] = tolower (opt_type_str[c]);
881 if (0 == strcasecmp ("latency", opt_type_str))
882 type = GNUNET_ATS_PREFERENCE_LATENCY;
883 else if (0 == strcasecmp ("bandwidth", opt_type_str))
884 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
889 _("Valid type required\n"));
894 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
898 _("Cannot connect to ATS service, exiting...\n"));
900 GNUNET_ATS_performance_change_preference (ph,
903 (double) opt_pref_value,
904 GNUNET_ATS_PREFERENCE_END);
906 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
911 if (NULL != cpid_str)
913 ats_ch = GNUNET_ATS_connectivity_init (cfg);
914 ats_sh = GNUNET_ATS_connectivity_suggest (ats_ch,
918 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
930 * @param argc number of arguments from the command line
931 * @param argv command line arguments
932 * @return 0 ok, 1 on error
940 opt_resolve_addresses_numeric = GNUNET_NO;
941 opt_monitor = GNUNET_NO;
942 opt_list_all = GNUNET_NO;
943 opt_list_used = GNUNET_NO;
944 opt_set_pref = GNUNET_NO;
946 stat_receive_done = GNUNET_NO;
949 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
951 gettext_noop ("get list of active addresses currently used"), 0,
952 &GNUNET_GETOPT_set_one, &opt_list_used },
953 { 'a', "all", NULL, gettext_noop ("get list of all active addresses"), 0,
954 &GNUNET_GETOPT_set_one, &opt_list_all },
955 { 'C', "connect", "PEER",
956 gettext_noop ("connect to PEER"), 1,
957 &GNUNET_GETOPT_set_string, &cpid_str },
958 { 'n', "numeric", NULL,
959 gettext_noop ("do not resolve IP addresses to hostnames"), 0,
960 &GNUNET_GETOPT_set_one, &opt_resolve_addresses_numeric },
961 { 'm', "monitor", NULL, gettext_noop ("monitor mode"), 0,
962 &GNUNET_GETOPT_set_one, &opt_monitor },
963 { 'p', "preference", NULL, gettext_noop ("set preference for the given peer"),
964 0, &GNUNET_GETOPT_set_one, &opt_set_pref },
965 { 'q', "quotas", NULL, gettext_noop ("print all configured quotas"), 0,
966 &GNUNET_GETOPT_set_one, &opt_print_quotas },
967 { 'i', "id", "TYPE", gettext_noop ("peer id"), 1, &GNUNET_GETOPT_set_string,
969 { 't', "type", "TYPE",
970 gettext_noop ("preference type to set: latency | bandwidth"), 1,
971 &GNUNET_GETOPT_set_string, &opt_type_str },
972 { 'k', "value", "VALUE", gettext_noop ("preference value"), 1,
973 &GNUNET_GETOPT_set_uint, &opt_pref_value },
974 { 'V', "verbose", NULL,
975 gettext_noop ("verbose output (include ATS address properties)"), 0,
976 &GNUNET_GETOPT_set_one, &opt_verbose },
977 GNUNET_GETOPT_OPTION_END
980 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
983 res = GNUNET_PROGRAM_run (argc, argv,
985 gettext_noop ("Print information about ATS state"),
988 GNUNET_free_non_null(opt_pid_str);
989 GNUNET_free_non_null(opt_type_str);
990 GNUNET_free((void *) argv);
992 if (GNUNET_OK == res)
998 /* end of gnunet-ats.c */