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.
17 * @file ats-tool/gnunet-ats.c
18 * @brief ATS command line tool
19 * @author Matthias Wachs
20 * @author Christian Grothoff
23 #include "gnunet_util_lib.h"
24 #include "gnunet_ats_service.h"
25 #include "gnunet_transport_service.h"
28 * String to respresent unlimited
30 #define UNLIMITED_STRING "unlimited"
36 static int opt_resolve_addresses_numeric;
39 * CLI Opt: Print verbose ATS information
41 static int opt_verbose;
44 * CLI Option: List only addresses currently used (active)
46 static int opt_list_used;
49 * CLI Option: List all addresses
51 static int opt_list_all;
54 * CLI Option: set preference
56 static int opt_set_pref;
59 * CLI Option: print quotas configured
61 static int opt_print_quotas;
64 * CLI Option: Monitor addresses used
66 static int opt_monitor;
69 * CLI Option: use specific peer
71 static char *opt_pid_str;
74 * CLI Option: preference type to set
76 static char *opt_type_str;
79 * CLI Option: preference value to set
81 static unsigned int opt_pref_value;
89 * Number of results returned from service
91 static int stat_results;
94 * State: all pending receive operations done?
96 static int stat_receive_done;
99 * State: number of pending operations
101 static int stat_pending;
104 * Which peer should we connect to?
106 static char *cpid_str;
109 * ATS performance handle used
111 static struct GNUNET_ATS_PerformanceHandle *ph;
114 * Our connectivity handle.
116 static struct GNUNET_ATS_ConnectivityHandle *ats_ch;
119 * Handle for address suggestion request.
121 static struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
124 * ATS address list handle used
126 static struct GNUNET_ATS_AddressListHandle *alh;
129 * Configuration handle
131 static struct GNUNET_CONFIGURATION_Handle *cfg;
136 static struct GNUNET_SCHEDULER_Task *shutdown_task;
139 * Hashmap to store addresses
141 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
145 * Structure used to remember all pending address resolutions.
146 * We keep address information in here while we talk to transport
147 * to map the address to a string.
149 struct PendingResolutions
154 struct PendingResolutions *next;
159 struct PendingResolutions *prev;
162 * Copy of the address we are resolving.
164 struct GNUNET_HELLO_Address *address;
167 * Handle to the transport request to convert the address
170 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
175 struct GNUNET_ATS_Properties properties;
178 * Amount of outbound bandwidth assigned by ATS.
180 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
183 * Amount of inbound bandwidth assigned by ATS.
185 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
188 * Is this an active address?
195 * Information we keep for an address. Used to avoid
196 * printing the same data multiple times.
201 * Address information.
203 struct GNUNET_HELLO_Address *address;
206 * Current outbound bandwidth.
208 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
211 * Current inbound bandwidth.
213 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
216 * Is this an active address?
224 * Head of list of pending resolution requests.
226 static struct PendingResolutions *head;
229 * Tail of list of pending resolution requests.
231 static struct PendingResolutions *tail;
235 * Free address corresponding to a given peer.
238 * @param key peer identity
239 * @param value the `struct ATSAddress *` to be freed
240 * @return #GNUNET_YES (always)
243 free_addr_it (void *cls,
244 const struct GNUNET_PeerIdentity *key,
247 struct ATSAddress *a = value;
249 GNUNET_assert (GNUNET_OK ==
250 GNUNET_CONTAINER_multipeermap_remove (addresses,
253 GNUNET_HELLO_address_free (a->address);
260 * Task run on shutdown.
267 struct PendingResolutions * pr;
268 struct PendingResolutions * next;
269 unsigned int pending;
273 GNUNET_ATS_performance_list_addresses_cancel (alh);
279 GNUNET_ATS_performance_done (ph);
285 while (NULL != (pr = next))
288 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
289 GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
290 GNUNET_free(pr->address);
294 GNUNET_CONTAINER_multipeermap_iterate (addresses,
297 GNUNET_CONTAINER_multipeermap_destroy (addresses);
302 _("%u address resolutions had a timeout\n"),
304 if (opt_list_used || opt_list_all)
306 _("ATS returned stat_results for %u addresses\n"),
311 GNUNET_ATS_connectivity_suggest_cancel (ats_sh);
316 GNUNET_ATS_connectivity_done (ats_ch);
324 * Function to call with a textual representation of an address. This
325 * function will be called several times with different possible
326 * textual representations, and a last time with @a address being NULL
327 * to signal the end of the iteration. Note that @a address NULL
328 * always is the last call, regardless of the value in @a res.
330 * @param cls closure, a `struct PendingResolutions *`
331 * @param address NULL on end of iteration,
332 * otherwise 0-terminated printable UTF-8 string,
333 * in particular an empty string if @a res is #GNUNET_NO
334 * @param res result of the address to string conversion:
335 * if #GNUNET_OK: conversion successful
336 * if #GNUNET_NO: address was invalid (or not supported)
337 * if #GNUNET_SYSERR: communication error (IPC error)
340 transport_addr_to_str_cb (void *cls,
344 struct PendingResolutions *pr = cls;
349 GNUNET_CONTAINER_DLL_remove (head,
352 GNUNET_free (pr->address);
356 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
358 /* All messages received and no resolutions pending*/
359 if (shutdown_task != NULL)
360 GNUNET_SCHEDULER_cancel (shutdown_task);
361 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
369 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
370 GNUNET_i2s (&pr->address->peer),
371 pr->address->transport_name,
372 (unsigned int) pr->address->address_length);
376 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
377 GNUNET_i2s (&pr->address->peer),
378 pr->address->transport_name,
379 (unsigned int) pr->address->address_length);
382 /* continues below */
390 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
391 GNUNET_i2s (&pr->address->peer),
392 pr->address->transport_name,
394 GNUNET_ATS_print_network_type (pr->properties.scope),
395 ntohl (pr->bandwidth_out.value__),
396 ntohl (pr->bandwidth_in.value__),
397 pr->active ? _("active ") : _("inactive "));
402 * Closure for #find_address_it().
404 struct AddressFindCtx
407 * Address we are looking for.
409 const struct GNUNET_HELLO_Address *src;
412 * Where to write the `struct ATSAddress` if we found one that matches.
414 struct ATSAddress *res;
419 * Find address corresponding to a given peer.
421 * @param cls the `struct AddressFindCtx *`
422 * @param key peer identity
423 * @param value the `struct ATSAddress *` for an existing address
424 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
427 find_address_it (void *cls,
428 const struct GNUNET_PeerIdentity *key,
431 struct AddressFindCtx *actx = cls;
432 struct ATSAddress *exist = value;
434 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
445 * Signature of a function that is called with QoS information about an address.
447 * @param cls closure (NULL)
448 * @param address the address, NULL if ATS service was disconnected
449 * @param active #GNUNET_YES if this address is actively used
450 * to maintain a connection to a peer;
451 * #GNUNET_NO if the address is not actively used;
452 * #GNUNET_SYSERR if this address is no longer available for ATS
453 * @param bandwidth_out assigned outbound bandwidth for the connection
454 * @param bandwidth_in assigned inbound bandwidth for the connection
455 * @param prop performance data for the address (as far as known)
458 ats_perf_mon_cb (void *cls,
459 const struct GNUNET_HELLO_Address *address,
461 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
462 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
463 const struct GNUNET_ATS_Properties *prop)
465 struct PendingResolutions *pr;
466 struct PendingResolutions *cur;
467 struct PendingResolutions *next;
471 /* ATS service temporarily disconnected, remove current state */
473 for (cur = next; NULL != cur; cur = next)
476 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
477 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
478 GNUNET_HELLO_address_free (cur->address);
481 GNUNET_CONTAINER_multipeermap_iterate (addresses,
486 if (GNUNET_SYSERR == active)
489 struct AddressFindCtx actx;
493 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
497 if (NULL == actx.res)
502 GNUNET_break(GNUNET_OK ==
503 GNUNET_CONTAINER_multipeermap_remove (addresses,
507 _("Removed address of peer `%s' with plugin `%s'\n"),
508 GNUNET_i2s (&address->peer),
509 actx.res->address->transport_name);
510 GNUNET_HELLO_address_free (actx.res);
514 if (GNUNET_NO == opt_verbose)
516 struct AddressFindCtx actx;
517 struct ATSAddress *a;
521 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
525 if ((NULL != actx.res))
527 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
528 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
529 (active == actx.res->active))
531 return; /* Nothing to do here */
535 actx.res->bandwidth_in = bandwidth_in;
536 actx.res->bandwidth_out = bandwidth_out;
541 a = GNUNET_new (struct ATSAddress);
543 a->address = GNUNET_HELLO_address_copy(address);
544 a->bandwidth_in = bandwidth_in;
545 a->bandwidth_out = bandwidth_out;
547 GNUNET_CONTAINER_multipeermap_put (addresses,
550 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
554 pr = GNUNET_new (struct PendingResolutions);
555 pr->properties = *prop;
556 pr->address = GNUNET_HELLO_address_copy (address);
557 pr->bandwidth_in = bandwidth_in;
558 pr->bandwidth_out = bandwidth_out;
560 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
561 opt_resolve_addresses_numeric,
562 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
563 &transport_addr_to_str_cb,
565 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
572 * Signature of a function that is called with QoS information about an address.
574 * @param cls closure (NULL)
575 * @param address the address, NULL if ATS service was disconnected
576 * @param active is this address actively used to maintain a connection
578 * @param bandwidth_out assigned outbound bandwidth for the connection
579 * @param bandwidth_in assigned inbound bandwidth for the connection
580 * @param prop performance data for the address (as far as known)
583 ats_perf_cb (void *cls,
584 const struct GNUNET_HELLO_Address *address,
586 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
587 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
588 const struct GNUNET_ATS_Properties *prop)
590 struct PendingResolutions *pr;
594 /* All messages received */
595 stat_receive_done = GNUNET_YES;
597 if (0 == stat_pending)
599 /* All messages received and no resolutions pending*/
600 if (shutdown_task != NULL)
601 GNUNET_SCHEDULER_cancel (shutdown_task);
602 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
607 pr = GNUNET_new (struct PendingResolutions);
608 pr->properties = *prop;
609 pr->address = GNUNET_HELLO_address_copy (address);
610 pr->bandwidth_in = bandwidth_in;
611 pr->bandwidth_out = bandwidth_out;
613 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
614 opt_resolve_addresses_numeric,
615 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
616 &transport_addr_to_str_cb, pr);
617 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
624 * Print information about the quotas configured for the various
627 * @param cfg configuration to obtain quota information from
628 * @return total number of ATS network types known
631 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
633 char * entry_in = NULL;
634 char * entry_out = NULL;
635 char * quota_out_str;
637 unsigned long long int quota_out;
638 unsigned long long int quota_in;
641 for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
644 GNUNET_asprintf (&entry_out,
646 GNUNET_ATS_print_network_type (c));
647 GNUNET_asprintf (&entry_in,
649 GNUNET_ATS_print_network_type (c));
653 GNUNET_CONFIGURATION_get_value_string (cfg,
658 if (0 == strcmp (quota_out_str, UNLIMITED_STRING)
660 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
662 quota_out = UINT32_MAX;
664 GNUNET_free(quota_out_str);
665 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
670 "Outbound quota for network `%11s' not configured!\n",
671 GNUNET_ATS_print_network_type (c));
672 GNUNET_asprintf ("a_out_str, "-");
674 GNUNET_free (entry_out);
678 GNUNET_CONFIGURATION_get_value_string (cfg,
683 if (0 == strcmp (quota_in_str, UNLIMITED_STRING)
685 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
686 quota_in = UINT32_MAX;
687 GNUNET_free (quota_in_str);
688 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
693 "Inbound quota for network `%11s' not configured!\n",
694 GNUNET_ATS_print_network_type (c));
695 GNUNET_asprintf ("a_in_str, "-");
697 GNUNET_free(entry_in);
700 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
701 GNUNET_ATS_print_network_type (c),
704 GNUNET_free(quota_out_str);
705 GNUNET_free(quota_in_str);
707 return GNUNET_ATS_NetworkTypeCount;
712 * Main function that will be run by the scheduler.
715 * @param args remaining command-line arguments
716 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
717 * @param my_cfg configuration
723 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
725 struct GNUNET_PeerIdentity pid;
726 struct GNUNET_PeerIdentity cpid;
730 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
731 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
735 if (NULL != opt_pid_str)
738 GNUNET_CRYPTO_eddsa_public_key_from_string (opt_pid_str,
739 strlen (opt_pid_str),
743 _("Failed to parse peer identity `%s'\n"),
748 if (NULL != cpid_str)
751 GNUNET_CRYPTO_eddsa_public_key_from_string (cpid_str,
756 _("Failed to parse peer identity `%s'\n"),
763 c += opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
768 _("Please select one operation: %s or %s or %s or %s or %s\n"),
777 opt_list_used = GNUNET_YES; /* set default */
778 if (opt_print_quotas)
780 ret = print_quotas (cfg);
785 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
790 _("Cannot connect to ATS service, exiting...\n"));
793 alh = GNUNET_ATS_performance_list_addresses (ph,
794 (NULL == opt_pid_str) ? NULL : &pid,
801 _("Cannot issue request to ATS service, exiting...\n"));
802 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
805 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
811 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
815 _("Cannot connect to ATS service, exiting...\n"));
817 alh = GNUNET_ATS_performance_list_addresses (ph,
818 (NULL == opt_pid_str)
827 _("Cannot issue request to ATS service, exiting...\n"));
828 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
831 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
837 ph = GNUNET_ATS_performance_init (cfg,
840 shutdown_task = GNUNET_SCHEDULER_add_shutdown (&end,
846 _("Cannot connect to ATS service, exiting...\n"));
847 GNUNET_SCHEDULER_shutdown ();
853 if (NULL == opt_type_str)
857 _("No preference type given!\n"));
860 if (NULL == opt_pid_str)
864 _("No peer given!\n"));
868 for (c = 0; c < strlen (opt_type_str); c++)
870 if (isupper ((unsigned char) opt_type_str[c]))
871 opt_type_str[c] = tolower ((unsigned char) opt_type_str[c]);
874 if (0 == strcasecmp ("latency", opt_type_str))
875 type = GNUNET_ATS_PREFERENCE_LATENCY;
876 else if (0 == strcasecmp ("bandwidth", opt_type_str))
877 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
882 _("Valid type required\n"));
887 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
891 _("Cannot connect to ATS service, exiting...\n"));
893 GNUNET_ATS_performance_change_preference (ph,
896 (double) opt_pref_value,
897 GNUNET_ATS_PREFERENCE_END);
899 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
904 if (NULL != cpid_str)
906 ats_ch = GNUNET_ATS_connectivity_init (cfg);
907 ats_sh = GNUNET_ATS_connectivity_suggest (ats_ch,
911 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
923 * @param argc number of arguments from the command line
924 * @param argv command line arguments
925 * @return 0 ok, 1 on error
933 opt_resolve_addresses_numeric = GNUNET_NO;
934 opt_monitor = GNUNET_NO;
935 opt_list_all = GNUNET_NO;
936 opt_list_used = GNUNET_NO;
937 opt_set_pref = GNUNET_NO;
939 stat_receive_done = GNUNET_NO;
942 struct GNUNET_GETOPT_CommandLineOption options[] = {
943 GNUNET_GETOPT_option_flag ('u',
945 gettext_noop ("get list of active addresses currently used"),
947 GNUNET_GETOPT_option_flag ('a',
949 gettext_noop ("get list of all active addresses"),
952 GNUNET_GETOPT_option_string ('C',
955 gettext_noop ("connect to PEER"),
957 GNUNET_GETOPT_option_flag ('n',
959 gettext_noop ("do not resolve IP addresses to hostnames"),
960 &opt_resolve_addresses_numeric),
962 GNUNET_GETOPT_option_flag ('m',
964 gettext_noop ("monitor mode"),
967 GNUNET_GETOPT_option_flag ('p',
969 gettext_noop ("set preference for the given peer"),
972 GNUNET_GETOPT_option_flag ('q',
974 gettext_noop ("print all configured quotas"),
976 GNUNET_GETOPT_option_string ('i',
979 gettext_noop ("peer id"),
982 GNUNET_GETOPT_option_string ('t',
985 gettext_noop ("preference type to set: latency | bandwidth"),
988 GNUNET_GETOPT_option_uint ('k',
991 gettext_noop ("preference value"),
994 GNUNET_GETOPT_option_flag ('V',
996 gettext_noop ("verbose output (include ATS address properties)"),
998 GNUNET_GETOPT_OPTION_END
1001 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1004 res = GNUNET_PROGRAM_run (argc, argv,
1006 gettext_noop ("Print information about ATS state"),
1009 GNUNET_free_non_null(opt_pid_str);
1010 GNUNET_free_non_null(opt_type_str);
1011 GNUNET_free((void *) argv);
1013 if (GNUNET_OK == res)
1019 /* end of gnunet-ats.c */