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/>.
20 * @file ats-tool/gnunet-ats.c
21 * @brief ATS command line tool
22 * @author Matthias Wachs
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet_transport_service.h"
31 * String to respresent unlimited
33 #define UNLIMITED_STRING "unlimited"
39 static int opt_resolve_addresses_numeric;
42 * CLI Opt: Print verbose ATS information
44 static int opt_verbose;
47 * CLI Option: List only addresses currently used (active)
49 static int opt_list_used;
52 * CLI Option: List all addresses
54 static int opt_list_all;
57 * CLI Option: set preference
59 static int opt_set_pref;
62 * CLI Option: print quotas configured
64 static int opt_print_quotas;
67 * CLI Option: Monitor addresses used
69 static int opt_monitor;
72 * CLI Option: use specific peer
74 static char *opt_pid_str;
77 * CLI Option: preference type to set
79 static char *opt_type_str;
82 * CLI Option: preference value to set
84 static unsigned int opt_pref_value;
92 * Number of results returned from service
94 static int stat_results;
97 * State: all pending receive operations done?
99 static int stat_receive_done;
102 * State: number of pending operations
104 static int stat_pending;
107 * Which peer should we connect to?
109 static char *cpid_str;
112 * ATS performance handle used
114 static struct GNUNET_ATS_PerformanceHandle *ph;
117 * Our connectivity handle.
119 static struct GNUNET_ATS_ConnectivityHandle *ats_ch;
122 * Handle for address suggestion request.
124 static struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
127 * ATS address list handle used
129 static struct GNUNET_ATS_AddressListHandle *alh;
132 * Configuration handle
134 static struct GNUNET_CONFIGURATION_Handle *cfg;
139 static struct GNUNET_SCHEDULER_Task *shutdown_task;
142 * Hashmap to store addresses
144 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
148 * Structure used to remember all pending address resolutions.
149 * We keep address information in here while we talk to transport
150 * to map the address to a string.
152 struct PendingResolutions
157 struct PendingResolutions *next;
162 struct PendingResolutions *prev;
165 * Copy of the address we are resolving.
167 struct GNUNET_HELLO_Address *address;
170 * Handle to the transport request to convert the address
173 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
178 struct GNUNET_ATS_Properties properties;
181 * Amount of outbound bandwidth assigned by ATS.
183 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
186 * Amount of inbound bandwidth assigned by ATS.
188 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
191 * Is this an active address?
198 * Information we keep for an address. Used to avoid
199 * 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?
227 * Head of list of pending resolution requests.
229 static struct PendingResolutions *head;
232 * Tail of list of pending resolution requests.
234 static struct PendingResolutions *tail;
238 * Free address corresponding to a given peer.
241 * @param key peer identity
242 * @param value the `struct ATSAddress *` to be freed
243 * @return #GNUNET_YES (always)
246 free_addr_it (void *cls,
247 const struct GNUNET_PeerIdentity *key,
250 struct ATSAddress *a = value;
252 GNUNET_assert (GNUNET_OK ==
253 GNUNET_CONTAINER_multipeermap_remove (addresses,
256 GNUNET_HELLO_address_free (a->address);
263 * Task run on shutdown.
270 struct PendingResolutions * pr;
271 struct PendingResolutions * next;
272 unsigned int pending;
276 GNUNET_ATS_performance_list_addresses_cancel (alh);
282 GNUNET_ATS_performance_done (ph);
288 while (NULL != (pr = next))
291 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
292 GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
293 GNUNET_free(pr->address);
297 GNUNET_CONTAINER_multipeermap_iterate (addresses,
300 GNUNET_CONTAINER_multipeermap_destroy (addresses);
305 _("%u address resolutions had a timeout\n"),
307 if (opt_list_used || opt_list_all)
309 _("ATS returned stat_results for %u addresses\n"),
314 GNUNET_ATS_connectivity_suggest_cancel (ats_sh);
319 GNUNET_ATS_connectivity_done (ats_ch);
327 * Function to call with a textual representation of an address. This
328 * function will be called several times with different possible
329 * textual representations, and a last time with @a address being NULL
330 * to signal the end of the iteration. Note that @a address NULL
331 * always is the last call, regardless of the value in @a res.
333 * @param cls closure, a `struct PendingResolutions *`
334 * @param address NULL on end of iteration,
335 * otherwise 0-terminated printable UTF-8 string,
336 * in particular an empty string if @a res is #GNUNET_NO
337 * @param res result of the address to string conversion:
338 * if #GNUNET_OK: conversion successful
339 * if #GNUNET_NO: address was invalid (or not supported)
340 * if #GNUNET_SYSERR: communication error (IPC error)
343 transport_addr_to_str_cb (void *cls,
347 struct PendingResolutions *pr = cls;
352 GNUNET_CONTAINER_DLL_remove (head,
355 GNUNET_free (pr->address);
359 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
361 /* All messages received and no resolutions pending*/
362 if (shutdown_task != NULL)
363 GNUNET_SCHEDULER_cancel (shutdown_task);
364 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
372 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
373 GNUNET_i2s (&pr->address->peer),
374 pr->address->transport_name,
375 (unsigned int) pr->address->address_length);
379 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
380 GNUNET_i2s (&pr->address->peer),
381 pr->address->transport_name,
382 (unsigned int) pr->address->address_length);
385 /* continues below */
393 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
394 GNUNET_i2s (&pr->address->peer),
395 pr->address->transport_name,
397 GNUNET_NT_to_string (pr->properties.scope),
398 ntohl (pr->bandwidth_out.value__),
399 ntohl (pr->bandwidth_in.value__),
400 pr->active ? _("active ") : _("inactive "));
405 * Closure for #find_address_it().
407 struct AddressFindCtx
410 * Address we are looking for.
412 const struct GNUNET_HELLO_Address *src;
415 * Where to write the `struct ATSAddress` if we found one that matches.
417 struct ATSAddress *res;
422 * Find address corresponding to a given peer.
424 * @param cls the `struct AddressFindCtx *`
425 * @param key peer identity
426 * @param value the `struct ATSAddress *` for an existing address
427 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
430 find_address_it (void *cls,
431 const struct GNUNET_PeerIdentity *key,
434 struct AddressFindCtx *actx = cls;
435 struct ATSAddress *exist = value;
437 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
448 * Signature of a function that is called with QoS information about an address.
450 * @param cls closure (NULL)
451 * @param address the address, NULL if ATS service was disconnected
452 * @param active #GNUNET_YES if this address is actively used
453 * to maintain a connection to a peer;
454 * #GNUNET_NO if the address is not actively used;
455 * #GNUNET_SYSERR if this address is no longer available for ATS
456 * @param bandwidth_out assigned outbound bandwidth for the connection
457 * @param bandwidth_in assigned inbound bandwidth for the connection
458 * @param prop performance data for the address (as far as known)
461 ats_perf_mon_cb (void *cls,
462 const struct GNUNET_HELLO_Address *address,
464 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
465 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
466 const struct GNUNET_ATS_Properties *prop)
468 struct PendingResolutions *pr;
469 struct PendingResolutions *cur;
470 struct PendingResolutions *next;
474 /* ATS service temporarily disconnected, remove current state */
476 for (cur = next; NULL != cur; cur = next)
479 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
480 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
481 GNUNET_HELLO_address_free (cur->address);
484 GNUNET_CONTAINER_multipeermap_iterate (addresses,
489 if (GNUNET_SYSERR == active)
492 struct AddressFindCtx actx;
496 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
500 if (NULL == actx.res)
505 GNUNET_break(GNUNET_OK ==
506 GNUNET_CONTAINER_multipeermap_remove (addresses,
510 _("Removed address of peer `%s' with plugin `%s'\n"),
511 GNUNET_i2s (&address->peer),
512 actx.res->address->transport_name);
513 GNUNET_HELLO_address_free (actx.res);
517 if (GNUNET_NO == opt_verbose)
519 struct AddressFindCtx actx;
520 struct ATSAddress *a;
524 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
528 if ((NULL != actx.res))
530 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
531 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
532 (active == actx.res->active))
534 return; /* Nothing to do here */
538 actx.res->bandwidth_in = bandwidth_in;
539 actx.res->bandwidth_out = bandwidth_out;
544 a = GNUNET_new (struct ATSAddress);
546 a->address = GNUNET_HELLO_address_copy(address);
547 a->bandwidth_in = bandwidth_in;
548 a->bandwidth_out = bandwidth_out;
550 GNUNET_CONTAINER_multipeermap_put (addresses,
553 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
557 pr = GNUNET_new (struct PendingResolutions);
558 pr->properties = *prop;
559 pr->address = GNUNET_HELLO_address_copy (address);
560 pr->bandwidth_in = bandwidth_in;
561 pr->bandwidth_out = bandwidth_out;
563 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
564 opt_resolve_addresses_numeric,
565 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
566 &transport_addr_to_str_cb,
568 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
575 * Signature of a function that is called with QoS information about an address.
577 * @param cls closure (NULL)
578 * @param address the address, NULL if ATS service was disconnected
579 * @param active is this address actively used to maintain a connection
581 * @param bandwidth_out assigned outbound bandwidth for the connection
582 * @param bandwidth_in assigned inbound bandwidth for the connection
583 * @param prop performance data for the address (as far as known)
586 ats_perf_cb (void *cls,
587 const struct GNUNET_HELLO_Address *address,
589 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
590 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
591 const struct GNUNET_ATS_Properties *prop)
593 struct PendingResolutions *pr;
597 /* All messages received */
598 stat_receive_done = GNUNET_YES;
600 if (0 == stat_pending)
602 /* All messages received and no resolutions pending*/
603 if (shutdown_task != NULL)
604 GNUNET_SCHEDULER_cancel (shutdown_task);
605 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
610 pr = GNUNET_new (struct PendingResolutions);
611 pr->properties = *prop;
612 pr->address = GNUNET_HELLO_address_copy (address);
613 pr->bandwidth_in = bandwidth_in;
614 pr->bandwidth_out = bandwidth_out;
616 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
617 opt_resolve_addresses_numeric,
618 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
619 &transport_addr_to_str_cb, pr);
620 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
627 * Print information about the quotas configured for the various
630 * @param cfg configuration to obtain quota information from
631 * @return total number of ATS network types known
634 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
636 char * entry_in = NULL;
637 char * entry_out = NULL;
638 char * quota_out_str;
640 unsigned long long int quota_out;
641 unsigned long long int quota_in;
644 for (c = 0; (c < GNUNET_NT_COUNT); c++)
647 GNUNET_asprintf (&entry_out,
649 GNUNET_NT_to_string (c));
650 GNUNET_asprintf (&entry_in,
652 GNUNET_NT_to_string (c));
656 GNUNET_CONFIGURATION_get_value_string (cfg,
661 if (0 == strcmp (quota_out_str, UNLIMITED_STRING)
663 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
665 quota_out = UINT32_MAX;
667 GNUNET_free(quota_out_str);
668 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
673 "Outbound quota for network `%11s' not configured!\n",
674 GNUNET_NT_to_string (c));
675 GNUNET_asprintf ("a_out_str, "-");
677 GNUNET_free (entry_out);
681 GNUNET_CONFIGURATION_get_value_string (cfg,
686 if (0 == strcmp (quota_in_str, UNLIMITED_STRING)
688 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
689 quota_in = UINT32_MAX;
690 GNUNET_free (quota_in_str);
691 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
696 "Inbound quota for network `%11s' not configured!\n",
697 GNUNET_NT_to_string (c));
698 GNUNET_asprintf ("a_in_str, "-");
700 GNUNET_free(entry_in);
703 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
704 GNUNET_NT_to_string (c),
707 GNUNET_free(quota_out_str);
708 GNUNET_free(quota_in_str);
710 return GNUNET_NT_COUNT;
715 * Main function that will be run by the scheduler.
718 * @param args remaining command-line arguments
719 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
720 * @param my_cfg configuration
726 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
728 struct GNUNET_PeerIdentity pid;
729 struct GNUNET_PeerIdentity cpid;
733 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
734 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
738 if (NULL != opt_pid_str)
741 GNUNET_CRYPTO_eddsa_public_key_from_string (opt_pid_str,
742 strlen (opt_pid_str),
746 _("Failed to parse peer identity `%s'\n"),
751 if (NULL != cpid_str)
754 GNUNET_CRYPTO_eddsa_public_key_from_string (cpid_str,
759 _("Failed to parse peer identity `%s'\n"),
766 c += opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
771 _("Please select one operation: %s or %s or %s or %s or %s\n"),
780 opt_list_used = GNUNET_YES; /* set default */
781 if (opt_print_quotas)
783 ret = print_quotas (cfg);
788 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
793 _("Cannot connect to ATS service, exiting...\n"));
796 alh = GNUNET_ATS_performance_list_addresses (ph,
797 (NULL == opt_pid_str) ? NULL : &pid,
804 _("Cannot issue request to ATS service, exiting...\n"));
805 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
808 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
814 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
818 _("Cannot connect to ATS service, exiting...\n"));
820 alh = GNUNET_ATS_performance_list_addresses (ph,
821 (NULL == opt_pid_str)
830 _("Cannot issue request to ATS service, exiting...\n"));
831 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
834 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
840 ph = GNUNET_ATS_performance_init (cfg,
843 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
849 _("Cannot connect to ATS service, exiting...\n"));
850 GNUNET_SCHEDULER_shutdown ();
856 if (NULL == opt_type_str)
860 _("No preference type given!\n"));
863 if (NULL == opt_pid_str)
867 _("No peer given!\n"));
871 for (c = 0; c < strlen (opt_type_str); c++)
873 if (isupper ((unsigned char) opt_type_str[c]))
874 opt_type_str[c] = tolower ((unsigned char) opt_type_str[c]);
877 if (0 == strcasecmp ("latency", opt_type_str))
878 type = GNUNET_ATS_PREFERENCE_LATENCY;
879 else if (0 == strcasecmp ("bandwidth", opt_type_str))
880 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
885 _("Valid type required\n"));
890 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
894 _("Cannot connect to ATS service, exiting...\n"));
896 GNUNET_ATS_performance_change_preference (ph,
899 (double) opt_pref_value,
900 GNUNET_ATS_PREFERENCE_END);
902 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
907 if (NULL != cpid_str)
909 ats_ch = GNUNET_ATS_connectivity_init (cfg);
910 ats_sh = GNUNET_ATS_connectivity_suggest (ats_ch,
914 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
926 * @param argc number of arguments from the command line
927 * @param argv command line arguments
928 * @return 0 ok, 1 on error
936 opt_resolve_addresses_numeric = GNUNET_NO;
937 opt_monitor = GNUNET_NO;
938 opt_list_all = GNUNET_NO;
939 opt_list_used = GNUNET_NO;
940 opt_set_pref = GNUNET_NO;
942 stat_receive_done = GNUNET_NO;
945 struct GNUNET_GETOPT_CommandLineOption options[] = {
946 GNUNET_GETOPT_option_flag ('u',
948 gettext_noop ("get list of active addresses currently used"),
950 GNUNET_GETOPT_option_flag ('a',
952 gettext_noop ("get list of all active addresses"),
955 GNUNET_GETOPT_option_string ('C',
958 gettext_noop ("connect to PEER"),
960 GNUNET_GETOPT_option_flag ('n',
962 gettext_noop ("do not resolve IP addresses to hostnames"),
963 &opt_resolve_addresses_numeric),
965 GNUNET_GETOPT_option_flag ('m',
967 gettext_noop ("monitor mode"),
970 GNUNET_GETOPT_option_flag ('p',
972 gettext_noop ("set preference for the given peer"),
975 GNUNET_GETOPT_option_flag ('q',
977 gettext_noop ("print all configured quotas"),
979 GNUNET_GETOPT_option_string ('i',
982 gettext_noop ("peer id"),
985 GNUNET_GETOPT_option_string ('t',
988 gettext_noop ("preference type to set: latency | bandwidth"),
991 GNUNET_GETOPT_option_uint ('k',
994 gettext_noop ("preference value"),
997 GNUNET_GETOPT_option_flag ('V',
999 gettext_noop ("verbose output (include ATS address properties)"),
1001 GNUNET_GETOPT_OPTION_END
1004 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1007 res = GNUNET_PROGRAM_run (argc, argv,
1009 gettext_noop ("Print information about ATS state"),
1012 GNUNET_free_non_null(opt_pid_str);
1013 GNUNET_free_non_null(opt_type_str);
1014 GNUNET_free((void *) argv);
1016 if (GNUNET_OK == res)
1022 /* end of gnunet-ats.c */