2 This file is part of GNUnet.
3 Copyright (C) 2009--2015 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 {
158 struct PendingResolutions *next;
163 struct PendingResolutions *prev;
166 * Copy of the address we are resolving.
168 struct GNUNET_HELLO_Address *address;
171 * Handle to the transport request to convert the address
174 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
179 struct GNUNET_ATS_Properties properties;
182 * Amount of outbound bandwidth assigned by ATS.
184 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
187 * Amount of inbound bandwidth assigned by ATS.
189 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
192 * Is this an active address?
199 * Information we keep for an address. Used to avoid
200 * printing the same data multiple times.
204 * Address information.
206 struct GNUNET_HELLO_Address *address;
209 * Current outbound bandwidth.
211 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
214 * Current inbound bandwidth.
216 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
219 * Is this an active address?
226 * Head of list of pending resolution requests.
228 static struct PendingResolutions *head;
231 * Tail of list of pending resolution requests.
233 static struct PendingResolutions *tail;
237 * Free address corresponding to a given peer.
240 * @param key peer identity
241 * @param value the `struct ATSAddress *` to be freed
242 * @return #GNUNET_YES (always)
245 free_addr_it(void *cls, const struct GNUNET_PeerIdentity *key, void *value)
247 struct ATSAddress *a = value;
249 GNUNET_assert(GNUNET_OK ==
250 GNUNET_CONTAINER_multipeermap_remove(addresses, key, value));
251 GNUNET_HELLO_address_free(a->address);
258 * Task run on shutdown.
265 struct PendingResolutions *pr;
266 struct PendingResolutions *next;
267 unsigned int pending;
271 GNUNET_ATS_performance_list_addresses_cancel(alh);
277 GNUNET_ATS_performance_done(ph);
283 while (NULL != (pr = next))
286 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
287 GNUNET_TRANSPORT_address_to_string_cancel(pr->tats_ctx);
288 GNUNET_free(pr->address);
292 GNUNET_CONTAINER_multipeermap_iterate(addresses, &free_addr_it, NULL);
293 GNUNET_CONTAINER_multipeermap_destroy(addresses);
297 fprintf(stdout, _("%u address resolutions had a timeout\n"), pending);
298 if (opt_list_used || opt_list_all)
300 _("ATS returned stat_results for %u addresses\n"),
305 GNUNET_ATS_connectivity_suggest_cancel(ats_sh);
310 GNUNET_ATS_connectivity_done(ats_ch);
318 * Function to call with a textual representation of an address. This
319 * function will be called several times with different possible
320 * textual representations, and a last time with @a address being NULL
321 * to signal the end of the iteration. Note that @a address NULL
322 * always is the last call, regardless of the value in @a res.
324 * @param cls closure, a `struct PendingResolutions *`
325 * @param address NULL on end of iteration,
326 * otherwise 0-terminated printable UTF-8 string,
327 * in particular an empty string if @a res is #GNUNET_NO
328 * @param res result of the address to string conversion:
329 * if #GNUNET_OK: conversion successful
330 * if #GNUNET_NO: address was invalid (or not supported)
331 * if #GNUNET_SYSERR: communication error (IPC error)
334 transport_addr_to_str_cb(void *cls, const char *address, int res)
336 struct PendingResolutions *pr = cls;
341 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
342 GNUNET_free(pr->address);
346 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
348 /* All messages received and no resolutions pending*/
349 if (shutdown_task != NULL)
350 GNUNET_SCHEDULER_cancel(shutdown_task);
351 shutdown_task = GNUNET_SCHEDULER_add_now(&end, NULL);
360 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
361 GNUNET_i2s(&pr->address->peer),
362 pr->address->transport_name,
363 (unsigned int)pr->address->address_length);
369 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
370 GNUNET_i2s(&pr->address->peer),
371 pr->address->transport_name,
372 (unsigned int)pr->address->address_length);
376 /* continues below */
387 "Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
388 GNUNET_i2s(&pr->address->peer),
389 pr->address->transport_name,
391 GNUNET_NT_to_string(pr->properties.scope),
392 ntohl(pr->bandwidth_out.value__),
393 ntohl(pr->bandwidth_in.value__),
394 pr->active ? _("active ") : _("inactive "));
399 * Closure for #find_address_it().
401 struct AddressFindCtx {
403 * Address we are looking for.
405 const struct GNUNET_HELLO_Address *src;
408 * Where to write the `struct ATSAddress` if we found one that matches.
410 struct ATSAddress *res;
415 * Find address corresponding to a given peer.
417 * @param cls the `struct AddressFindCtx *`
418 * @param key peer identity
419 * @param value the `struct ATSAddress *` for an existing address
420 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
423 find_address_it(void *cls, const struct GNUNET_PeerIdentity *key, void *value)
425 struct AddressFindCtx *actx = cls;
426 struct ATSAddress *exist = value;
428 if (0 == GNUNET_HELLO_address_cmp(actx->src, exist->address))
438 * Signature of a function that is called with QoS information about an address.
440 * @param cls closure (NULL)
441 * @param address the address, NULL if ATS service was disconnected
442 * @param active #GNUNET_YES if this address is actively used
443 * to maintain a connection to a peer;
444 * #GNUNET_NO if the address is not actively used;
445 * #GNUNET_SYSERR if this address is no longer available for ATS
446 * @param bandwidth_out assigned outbound bandwidth for the connection
447 * @param bandwidth_in assigned inbound bandwidth for the connection
448 * @param prop performance data for the address (as far as known)
451 ats_perf_mon_cb(void *cls,
452 const struct GNUNET_HELLO_Address *address,
454 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
455 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
456 const struct GNUNET_ATS_Properties *prop)
458 struct PendingResolutions *pr;
459 struct PendingResolutions *cur;
460 struct PendingResolutions *next;
464 /* ATS service temporarily disconnected, remove current state */
466 for (cur = next; NULL != cur; cur = next)
469 GNUNET_CONTAINER_DLL_remove(head, tail, cur);
470 GNUNET_TRANSPORT_address_to_string_cancel(cur->tats_ctx);
471 GNUNET_HELLO_address_free(cur->address);
474 GNUNET_CONTAINER_multipeermap_iterate(addresses, &free_addr_it, NULL);
477 if (GNUNET_SYSERR == active)
480 struct AddressFindCtx actx;
484 GNUNET_CONTAINER_multipeermap_get_multiple(addresses,
488 if (NULL == actx.res)
493 GNUNET_break(GNUNET_OK ==
494 GNUNET_CONTAINER_multipeermap_remove(addresses,
498 _("Removed address of peer `%s' with plugin `%s'\n"),
499 GNUNET_i2s(&address->peer),
500 actx.res->address->transport_name);
501 GNUNET_HELLO_address_free(actx.res);
505 if (GNUNET_NO == opt_verbose)
507 struct AddressFindCtx actx;
508 struct ATSAddress *a;
512 GNUNET_CONTAINER_multipeermap_get_multiple(addresses,
516 if ((NULL != actx.res))
518 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
519 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
520 (active == actx.res->active))
522 return; /* Nothing to do here */
526 actx.res->bandwidth_in = bandwidth_in;
527 actx.res->bandwidth_out = bandwidth_out;
532 a = GNUNET_new(struct ATSAddress);
534 a->address = GNUNET_HELLO_address_copy(address);
535 a->bandwidth_in = bandwidth_in;
536 a->bandwidth_out = bandwidth_out;
538 GNUNET_CONTAINER_multipeermap_put(
542 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
546 pr = GNUNET_new(struct PendingResolutions);
547 pr->properties = *prop;
548 pr->address = GNUNET_HELLO_address_copy(address);
549 pr->bandwidth_in = bandwidth_in;
550 pr->bandwidth_out = bandwidth_out;
552 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string(
555 opt_resolve_addresses_numeric,
556 GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10),
557 &transport_addr_to_str_cb,
559 GNUNET_CONTAINER_DLL_insert(head, tail, pr);
566 * Signature of a function that is called with QoS information about an address.
568 * @param cls closure (NULL)
569 * @param address the address, NULL if ATS service was disconnected
570 * @param active is this address actively used to maintain a connection
572 * @param bandwidth_out assigned outbound bandwidth for the connection
573 * @param bandwidth_in assigned inbound bandwidth for the connection
574 * @param prop performance data for the address (as far as known)
577 ats_perf_cb(void *cls,
578 const struct GNUNET_HELLO_Address *address,
580 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
581 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
582 const struct GNUNET_ATS_Properties *prop)
584 struct PendingResolutions *pr;
588 /* All messages received */
589 stat_receive_done = GNUNET_YES;
591 if (0 == stat_pending)
593 /* All messages received and no resolutions pending*/
594 if (shutdown_task != NULL)
595 GNUNET_SCHEDULER_cancel(shutdown_task);
596 shutdown_task = GNUNET_SCHEDULER_add_now(&end, NULL);
601 pr = GNUNET_new(struct PendingResolutions);
602 pr->properties = *prop;
603 pr->address = GNUNET_HELLO_address_copy(address);
604 pr->bandwidth_in = bandwidth_in;
605 pr->bandwidth_out = bandwidth_out;
607 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string(
610 opt_resolve_addresses_numeric,
611 GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10),
612 &transport_addr_to_str_cb,
614 GNUNET_CONTAINER_DLL_insert(head, tail, pr);
621 * Print information about the quotas configured for the various
624 * @param cfg configuration to obtain quota information from
625 * @return total number of ATS network types known
628 print_quotas(const struct GNUNET_CONFIGURATION_Handle *cfg)
630 char *entry_in = NULL;
631 char *entry_out = NULL;
634 unsigned long long int quota_out;
635 unsigned long long int quota_in;
638 for (c = 0; (c < GNUNET_NT_COUNT); c++)
640 GNUNET_asprintf(&entry_out, "%s_QUOTA_OUT", GNUNET_NT_to_string(c));
641 GNUNET_asprintf(&entry_in, "%s_QUOTA_IN", GNUNET_NT_to_string(c));
644 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg,
649 if (0 == strcmp(quota_out_str, UNLIMITED_STRING) ||
651 GNUNET_STRINGS_fancy_size_to_bytes(quota_out_str, "a_out)))
652 quota_out = UINT32_MAX;
654 GNUNET_free(quota_out_str);
655 GNUNET_asprintf("a_out_str, "%llu", quota_out);
660 "Outbound quota for network `%11s' not configured!\n",
661 GNUNET_NT_to_string(c));
662 GNUNET_asprintf("a_out_str, "-");
664 GNUNET_free(entry_out);
667 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg,
672 if (0 == strcmp(quota_in_str, UNLIMITED_STRING) ||
674 GNUNET_STRINGS_fancy_size_to_bytes(quota_in_str, "a_in)))
675 quota_in = UINT32_MAX;
676 GNUNET_free(quota_in_str);
677 GNUNET_asprintf("a_in_str, "%llu", quota_in);
682 "Inbound quota for network `%11s' not configured!\n",
683 GNUNET_NT_to_string(c));
684 GNUNET_asprintf("a_in_str, "-");
686 GNUNET_free(entry_in);
689 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
690 GNUNET_NT_to_string(c),
693 GNUNET_free(quota_out_str);
694 GNUNET_free(quota_in_str);
696 return GNUNET_NT_COUNT;
701 * Main function that will be run by the scheduler.
704 * @param args remaining command-line arguments
705 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
706 * @param my_cfg configuration
712 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
714 struct GNUNET_PeerIdentity pid;
715 struct GNUNET_PeerIdentity cpid;
719 cfg = (struct GNUNET_CONFIGURATION_Handle *)my_cfg;
720 addresses = GNUNET_CONTAINER_multipeermap_create(10, GNUNET_NO);
724 if (NULL != opt_pid_str)
727 GNUNET_CRYPTO_eddsa_public_key_from_string(opt_pid_str,
731 fprintf(stderr, _("Failed to parse peer identity `%s'\n"), opt_pid_str);
735 if (NULL != cpid_str)
738 GNUNET_CRYPTO_eddsa_public_key_from_string(cpid_str,
742 fprintf(stderr, _("Failed to parse peer identity `%s'\n"), cpid_str);
748 c += opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
753 _("Please select one operation: %s or %s or %s or %s or %s\n"),
762 opt_list_used = GNUNET_YES; /* set default */
763 if (opt_print_quotas)
765 ret = print_quotas(cfg);
770 ph = GNUNET_ATS_performance_init(cfg, NULL, NULL);
773 fprintf(stderr, "%s", _("Cannot connect to ATS service, exiting...\n"));
776 alh = GNUNET_ATS_performance_list_addresses(ph,
777 (NULL == opt_pid_str) ? NULL
786 _("Cannot issue request to ATS service, exiting...\n"));
787 shutdown_task = GNUNET_SCHEDULER_add_now(&end, NULL);
790 shutdown_task = GNUNET_SCHEDULER_add_shutdown(&end, NULL);
795 ph = GNUNET_ATS_performance_init(cfg, NULL, NULL);
797 fprintf(stderr, "%s", _("Cannot connect to ATS service, exiting...\n"));
799 alh = GNUNET_ATS_performance_list_addresses(ph,
800 (NULL == opt_pid_str) ? NULL
809 _("Cannot issue request to ATS service, exiting...\n"));
810 shutdown_task = GNUNET_SCHEDULER_add_now(&end, NULL);
813 shutdown_task = GNUNET_SCHEDULER_add_shutdown(&end, NULL);
818 ph = GNUNET_ATS_performance_init(cfg, &ats_perf_mon_cb, NULL);
819 shutdown_task = GNUNET_SCHEDULER_add_shutdown(&end, NULL);
822 fprintf(stderr, "%s", _("Cannot connect to ATS service, exiting...\n"));
823 GNUNET_SCHEDULER_shutdown();
829 if (NULL == opt_type_str)
831 fprintf(stderr, "%s", _("No preference type given!\n"));
834 if (NULL == opt_pid_str)
836 fprintf(stderr, "%s", _("No peer given!\n"));
840 for (c = 0; c < strlen(opt_type_str); c++)
842 if (isupper((unsigned char)opt_type_str[c]))
843 opt_type_str[c] = tolower((unsigned char)opt_type_str[c]);
846 if (0 == strcasecmp("latency", opt_type_str))
847 type = GNUNET_ATS_PREFERENCE_LATENCY;
848 else if (0 == strcasecmp("bandwidth", opt_type_str))
849 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
852 fprintf(stderr, "%s", _("Valid type required\n"));
857 ph = GNUNET_ATS_performance_init(cfg, NULL, NULL);
859 fprintf(stderr, "%s", _("Cannot connect to ATS service, exiting...\n"));
861 GNUNET_ATS_performance_change_preference(ph,
864 (double)opt_pref_value,
865 GNUNET_ATS_PREFERENCE_END);
868 GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, &end, NULL);
871 if (NULL != cpid_str)
873 ats_ch = GNUNET_ATS_connectivity_init(cfg);
874 ats_sh = GNUNET_ATS_connectivity_suggest(ats_ch, &cpid, 1000);
876 GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, &end, NULL);
886 * @param argc number of arguments from the command line
887 * @param argv command line arguments
888 * @return 0 ok, 1 on error
891 main(int argc, char *const *argv)
895 opt_resolve_addresses_numeric = GNUNET_NO;
896 opt_monitor = GNUNET_NO;
897 opt_list_all = GNUNET_NO;
898 opt_list_used = GNUNET_NO;
899 opt_set_pref = GNUNET_NO;
901 stat_receive_done = GNUNET_NO;
904 struct GNUNET_GETOPT_CommandLineOption options[] =
905 { GNUNET_GETOPT_option_flag('u',
908 "get list of active addresses currently used"),
910 GNUNET_GETOPT_option_flag('a',
913 "get list of all active addresses"),
916 GNUNET_GETOPT_option_string('C',
919 gettext_noop("connect to PEER"),
921 GNUNET_GETOPT_option_flag('n',
924 "do not resolve IP addresses to hostnames"),
925 &opt_resolve_addresses_numeric),
927 GNUNET_GETOPT_option_flag('m',
929 gettext_noop("monitor mode"),
932 GNUNET_GETOPT_option_flag('p',
935 "set preference for the given peer"),
938 GNUNET_GETOPT_option_flag('q',
940 gettext_noop("print all configured quotas"),
942 GNUNET_GETOPT_option_string('i',
945 gettext_noop("peer id"),
948 GNUNET_GETOPT_option_string('t',
952 "preference type to set: latency | bandwidth"),
955 GNUNET_GETOPT_option_uint('k',
958 gettext_noop("preference value"),
961 GNUNET_GETOPT_option_flag(
964 gettext_noop("verbose output (include ATS address properties)"),
966 GNUNET_GETOPT_OPTION_END };
968 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args(argc, argv, &argc, &argv))
971 res = GNUNET_PROGRAM_run(argc,
974 gettext_noop("Print information about ATS state"),
978 GNUNET_free_non_null(opt_pid_str);
979 GNUNET_free_non_null(opt_type_str);
980 GNUNET_free((void *)argv);
982 if (GNUNET_OK == res)
988 /* end of gnunet-ats.c */