2 This file is part of GNUnet.
3 (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;
95 * Number of results returned from service
97 static int stat_results;
100 * State: all pending receive operations done?
102 static int stat_receive_done;
105 * State: number of pending operations
107 static int stat_pending;
110 * ATS performance handle used
112 static struct GNUNET_ATS_PerformanceHandle *ph;
115 * ATS address list handle used
117 static struct GNUNET_ATS_AddressListHandle *alh;
120 * Configuration handle
122 static struct GNUNET_CONFIGURATION_Handle *cfg;
127 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
130 * Hashmap to store addresses
132 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
136 * Structure used to remember all pending address resolutions.
137 * We keep address information in here while we talk to transport
138 * to map the address to a string.
140 struct PendingResolutions
145 struct PendingResolutions *next;
150 struct PendingResolutions *prev;
153 * Copy of the address we are resolving.
155 struct GNUNET_HELLO_Address *address;
158 * Handle to the transport request to convert the address
161 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
164 * Array of performance data.
166 struct GNUNET_ATS_Information *ats;
169 * Length of the @e ats array.
174 * Amount of outbound bandwidth assigned by ATS.
176 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
179 * Amount of inbound bandwidth assigned by ATS.
181 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
184 * Is this an active address?
191 * Information we keep for an address. Used to avoid
192 * printing the same data multiple times.
197 * Address information.
199 struct GNUNET_HELLO_Address *address;
202 * Current outbound bandwidth.
204 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
207 * Current inbound bandwidth.
209 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
212 * Is this an active address?
220 * Head of list of pending resolution requests.
222 static struct PendingResolutions *head;
225 * Tail of list of pending resolution requests.
227 static struct PendingResolutions *tail;
231 * Free address corresponding to a given peer.
234 * @param key peer identity
235 * @param value the `struct ATSAddress *` to be freed
236 * @return #GNUNET_YES (always)
239 free_addr_it (void *cls,
240 const struct GNUNET_PeerIdentity *key,
243 struct ATSAddress *a = value;
244 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (addresses, key, value));
245 GNUNET_HELLO_address_free (a->address);
252 * Task run on shutdown.
255 * @param tc scheduler context
259 const struct GNUNET_SCHEDULER_TaskContext *tc)
261 struct PendingResolutions * pr;
262 struct PendingResolutions * next;
263 unsigned int pending;
267 GNUNET_ATS_performance_list_addresses_cancel (alh);
273 GNUNET_ATS_performance_done (ph);
279 while (NULL != (pr = next))
282 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
283 GNUNET_TRANSPORT_address_to_string_cancel (pr->tats_ctx);
284 GNUNET_free(pr->address);
288 GNUNET_CONTAINER_multipeermap_iterate (addresses,
291 GNUNET_CONTAINER_multipeermap_destroy (addresses);
296 _("%u address resolutions had a timeout\n"),
298 if (opt_list_used || opt_list_all)
300 _("ATS returned stat_results for %u addresses\n"),
307 * Function to call with a textual representation of an address. This
308 * function will be called several times with different possible
309 * textual representations, and a last time with @a address being NULL
310 * to signal the end of the iteration. Note that @a address NULL
311 * always is the last call, regardless of the value in @a res.
313 * @param cls closure, a `struct PendingResolutions *`
314 * @param address NULL on end of iteration,
315 * otherwise 0-terminated printable UTF-8 string,
316 * in particular an empty string if @a res is #GNUNET_NO
317 * @param res result of the address to string conversion:
318 * if #GNUNET_OK: conversion successful
319 * if #GNUNET_NO: address was invalid (or not supported)
320 * if #GNUNET_SYSERR: communication error (IPC error)
323 transport_addr_to_str_cb (void *cls,
327 struct PendingResolutions *pr = cls;
330 char *ats_prop_arr[GNUNET_ATS_PropertyCount] = GNUNET_ATS_PropertyStrings;
331 char *ats_prop_value;
340 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
341 GNUNET_free(pr->address);
345 if ((GNUNET_YES == stat_receive_done) && (0 == stat_pending))
347 /* All messages received and no resolutions pending*/
348 if (shutdown_task != GNUNET_SCHEDULER_NO_TASK)
349 GNUNET_SCHEDULER_cancel (shutdown_task);
350 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
358 "Failed to convert address for peer `%s' plugin `%s' length %u to string (communication error)\n",
359 GNUNET_i2s (&pr->address->peer),
360 pr->address->transport_name,
361 (unsigned int) pr->address->address_length);
365 "Failed to convert address for peer `%s' plugin `%s' length %u to string (address invalid or not supported)\n",
366 GNUNET_i2s (&pr->address->peer),
367 pr->address->transport_name,
368 (unsigned int) pr->address->address_length);
371 /* continues below */
378 ats_str = GNUNET_strdup (pr->active ? _("active ") : _("inactive "));
379 network = GNUNET_ATS_NET_UNSPECIFIED;
380 for (c = 0; c < pr->ats_count; c++)
384 ats_type = ntohl (pr->ats[c].type);
385 ats_value = ntohl (pr->ats[c].value);
387 if (ats_type > GNUNET_ATS_PropertyCount)
390 "Invalid ATS property type %u %u for address %s\n",
399 case GNUNET_ATS_NETWORK_TYPE:
400 if (ats_value > GNUNET_ATS_NetworkTypeCount)
406 GNUNET_asprintf (&ats_prop_value,
408 GNUNET_ATS_print_network_type (ats_value));
411 GNUNET_asprintf (&ats_prop_value, "%u", ats_value);
414 if ((opt_verbose) && (ats_type < GNUNET_ATS_PropertyCount))
416 GNUNET_asprintf (&ats_str,
419 ats_prop_arr[ats_type],
421 GNUNET_free(ats_tmp);
423 GNUNET_free(ats_prop_value);
427 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
428 GNUNET_i2s (&pr->address->peer),
429 pr->address->transport_name,
431 GNUNET_ATS_print_network_type (network),
432 ntohl (pr->bandwidth_out.value__),
433 ntohl (pr->bandwidth_in.value__),
435 GNUNET_free (ats_str);
440 * Closure for #find_address_it().
442 struct AddressFindCtx
445 * Address we are looking for.
447 const struct GNUNET_HELLO_Address *src;
450 * Where to write the `struct ATSAddress` if we found one that matches.
452 struct ATSAddress *res;
457 * Find address corresponding to a given peer.
459 * @param cls the `struct AddressFindCtx *`
460 * @param key peer identity
461 * @param value the `struct ATSAddress *` for an existing address
462 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
465 find_address_it (void *cls,
466 const struct GNUNET_PeerIdentity *key,
469 struct AddressFindCtx *actx = cls;
470 struct ATSAddress *exist = value;
472 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
483 * Signature of a function that is called with QoS information about an address.
485 * @param cls closure (NULL)
486 * @param address the address, NULL if ATS service was disconnected
487 * @param active #GNUNET_YES if this address is actively used
488 * to maintain a connection to a peer;
489 * #GNUNET_NO if the address is not actively used;
490 * #GNUNET_SYSERR if this address is no longer available for ATS
491 * @param bandwidth_out assigned outbound bandwidth for the connection
492 * @param bandwidth_in assigned inbound bandwidth for the connection
493 * @param ats performance data for the address (as far as known)
494 * @param ats_count number of performance records in @a ats
497 ats_perf_mon_cb (void *cls,
498 const struct GNUNET_HELLO_Address *address,
500 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
501 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
502 const struct GNUNET_ATS_Information *ats,
505 struct PendingResolutions *pr;
506 struct PendingResolutions *cur;
507 struct PendingResolutions *next;
511 /* ATS service temporarily disconnected, remove current state */
513 for (cur = next; NULL != cur; cur = next)
516 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
517 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
518 GNUNET_HELLO_address_free (cur->address);
522 GNUNET_CONTAINER_multipeermap_iterate (addresses,
527 if (GNUNET_SYSERR == active)
530 struct AddressFindCtx actx;
534 GNUNET_CONTAINER_multipeermap_get_multiple (addresses, &address->peer,
535 &find_address_it, &actx);
536 if (NULL == actx.res)
542 GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (addresses, &address->peer, actx.res));
544 _("Removed address of peer `%s' with plugin `%s'\n"),
545 GNUNET_i2s (&address->peer),
546 actx.res->address->transport_name);
547 GNUNET_HELLO_address_free (actx.res);
548 GNUNET_free (actx.res);
552 if (GNUNET_NO == opt_verbose)
554 struct AddressFindCtx actx;
555 struct ATSAddress *a;
559 GNUNET_CONTAINER_multipeermap_get_multiple (addresses, &address->peer,
560 &find_address_it, &actx);
561 if ((NULL != actx.res))
563 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
564 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
565 (active == actx.res->active))
567 return; /* Nothing to do here */
571 actx.res->bandwidth_in = bandwidth_in;
572 actx.res->bandwidth_out = bandwidth_out;
577 a = GNUNET_new (struct ATSAddress);
579 a->address = GNUNET_HELLO_address_copy(address);
580 a->bandwidth_in = bandwidth_in;
581 a->bandwidth_out = bandwidth_out;
583 GNUNET_CONTAINER_multipeermap_put (addresses, &address->peer, a,
584 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
588 pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
589 ats_count * sizeof (struct GNUNET_ATS_Information));
591 pr->ats_count = ats_count;
592 pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
594 memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
595 pr->address = GNUNET_HELLO_address_copy (address);
596 pr->bandwidth_in = bandwidth_in;
597 pr->bandwidth_out = bandwidth_out;
599 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
600 opt_resolve_addresses_numeric,
601 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
602 &transport_addr_to_str_cb,
604 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
611 * Signature of a function that is called with QoS information about an address.
613 * @param cls closure (NULL)
614 * @param address the address, NULL if ATS service was disconnected
615 * @param active is this address actively used to maintain a connection
617 * @param bandwidth_out assigned outbound bandwidth for the connection
618 * @param bandwidth_in assigned inbound bandwidth for the connection
619 * @param ats performance data for the address (as far as known)
620 * @param ats_count number of performance records in @a ats
623 ats_perf_cb (void *cls,
624 const struct GNUNET_HELLO_Address *address,
626 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
627 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
628 const struct GNUNET_ATS_Information *ats,
631 struct PendingResolutions *pr;
635 /* All messages received */
636 stat_receive_done = GNUNET_YES;
638 if (0 == stat_pending)
640 /* All messages received and no resolutions pending*/
641 if (shutdown_task != GNUNET_SCHEDULER_NO_TASK)
642 GNUNET_SCHEDULER_cancel (shutdown_task);
643 shutdown_task = GNUNET_SCHEDULER_add_now (end, NULL);
648 pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
649 ats_count * sizeof (struct GNUNET_ATS_Information));
651 pr->ats_count = ats_count;
652 pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
654 memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
655 pr->address = GNUNET_HELLO_address_copy (address);
656 pr->bandwidth_in = bandwidth_in;
657 pr->bandwidth_out = bandwidth_out;
659 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
660 opt_resolve_addresses_numeric,
661 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
662 &transport_addr_to_str_cb, pr);
663 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
670 * Print information about the quotas configured for the various
673 * @param cfg configuration to obtain quota information from
674 * @return total number of ATS network types known
677 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
679 char *network_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
680 char * entry_in = NULL;
681 char * entry_out = NULL;
682 char * quota_out_str;
684 unsigned long long int quota_out;
685 unsigned long long int quota_in;
688 for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
691 GNUNET_asprintf (&entry_out,
694 GNUNET_asprintf (&entry_in,
700 GNUNET_CONFIGURATION_get_value_string (cfg,
705 if (0 == strcmp (quota_out_str, UNLIMITED_STRING)
707 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
709 quota_out = UINT32_MAX;
711 GNUNET_free(quota_out_str);
712 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
717 "Outbound quota for network `%11s' not configured!\n",
719 GNUNET_asprintf ("a_out_str, "-");
721 GNUNET_free(entry_out);
725 GNUNET_CONFIGURATION_get_value_string (cfg,
730 if (0 == strcmp (quota_in_str, UNLIMITED_STRING)
732 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
733 quota_in = UINT32_MAX;
734 GNUNET_free(quota_in_str);
735 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
740 "Inbound quota for network `%11s' not configured!\n",
742 GNUNET_asprintf ("a_in_str, "-");
744 GNUNET_free(entry_in);
747 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
751 GNUNET_free(quota_out_str);
752 GNUNET_free(quota_in_str);
754 return GNUNET_ATS_NetworkTypeCount;
759 * Function called with the result from the test if ATS is
760 * running. Runs the actual main logic.
762 * @param cls the `struct GNUNET_CONFIGURATION_Handle *`
763 * @param result result of the test, #GNUNET_YES if ATS is running
766 testservice_ats (void *cls,
769 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
770 struct GNUNET_PeerIdentity pid;
774 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
776 if (GNUNET_YES != result)
779 _("Service `%s' is not running\n"),
786 if (NULL != opt_pid_str)
789 != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_pid_str,
790 strlen (opt_pid_str), &pid.public_key))
793 _("Failed to parse peer identity `%s'\n"),
799 c = opt_list_all + opt_list_used + opt_monitor + opt_set_pref;
803 _("Please select one operation : %s or %s or %s or %s or %s\n"),
812 opt_list_used = GNUNET_YES; /* set default */
813 if (opt_print_quotas)
815 ret = print_quotas (cfg);
820 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
825 _("Cannot connect to ATS service, exiting...\n"));
829 alh = GNUNET_ATS_performance_list_addresses (ph,
830 (NULL == opt_pid_str) ? NULL : &pid,
837 _("Cannot issue request to ATS service, exiting...\n"));
838 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
841 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
845 else if (opt_list_used)
847 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
851 _("Cannot connect to ATS service, exiting...\n"));
853 alh = GNUNET_ATS_performance_list_addresses (ph,
854 (NULL == opt_pid_str)
863 _("Cannot issue request to ATS service, exiting...\n"));
864 shutdown_task = GNUNET_SCHEDULER_add_now (&end, NULL);
867 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
871 else if (opt_monitor)
873 ph = GNUNET_ATS_performance_init (cfg,
879 _("Cannot connect to ATS service, exiting...\n"));
880 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
885 else if (opt_set_pref)
887 if (NULL == opt_type_str)
891 _("No preference type given!\n"));
894 if (NULL == opt_pid_str)
898 _("No peer given!\n"));
902 for (c = 0; c < strlen (opt_type_str); c++)
904 if (isupper (opt_type_str[c]))
905 opt_type_str[c] = tolower (opt_type_str[c]);
908 if (0 == strcasecmp ("latency", opt_type_str))
909 type = GNUNET_ATS_PREFERENCE_LATENCY;
910 else if (0 == strcasecmp ("bandwidth", opt_type_str))
911 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
916 _("Valid type required\n"));
921 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
925 _("Cannot connect to ATS service, exiting...\n"));
927 GNUNET_ATS_performance_change_preference (ph, &pid, type, (double) opt_pref_value,
928 GNUNET_ATS_PREFERENCE_END);
930 shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
939 * Main function that will be run by the scheduler.
942 * @param args remaining command-line arguments
943 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
944 * @param my_cfg configuration
950 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
952 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
953 GNUNET_CLIENT_service_test ("ats", cfg,
954 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
955 &testservice_ats, (void *) cfg);
962 * @param argc number of arguments from the command line
963 * @param argv command line arguments
964 * @return 0 ok, 1 on error
972 opt_resolve_addresses_numeric = GNUNET_NO;
973 opt_monitor = GNUNET_NO;
974 opt_list_all = GNUNET_NO;
975 opt_list_used = GNUNET_NO;
976 opt_set_pref = GNUNET_NO;
978 stat_receive_done = GNUNET_NO;
981 static const struct GNUNET_GETOPT_CommandLineOption options[] =
984 gettext_noop ("get list of active addresses currently used"), 0,
985 &GNUNET_GETOPT_set_one, &opt_list_used },
986 { 'a', "all", NULL, gettext_noop ("get list of all active addresses"), 0,
987 &GNUNET_GETOPT_set_one, &opt_list_all },
988 { 'n', "numeric", NULL,
989 gettext_noop ("do not resolve IP addresses to hostnames"), 0,
990 &GNUNET_GETOPT_set_one, &opt_resolve_addresses_numeric },
991 { 'm', "monitor", NULL, gettext_noop ("monitor mode"), 0,
992 &GNUNET_GETOPT_set_one, &opt_monitor },
993 { 'p', "preference", NULL, gettext_noop ("set preference for the given peer"),
994 0, &GNUNET_GETOPT_set_one, &opt_set_pref },
995 { 'q', "quotas", NULL, gettext_noop ("print all configured quotas"), 0,
996 &GNUNET_GETOPT_set_one, &opt_print_quotas },
997 { 'i', "id", "TYPE", gettext_noop ("peer id"), 1, &GNUNET_GETOPT_set_string,
999 { 't', "type", "TYPE",
1000 gettext_noop ("preference type to set: latency | bandwidth"), 1,
1001 &GNUNET_GETOPT_set_string, &opt_type_str },
1002 { 'k', "value", "VALUE", gettext_noop ("preference value"), 1,
1003 &GNUNET_GETOPT_set_uint, &opt_pref_value },
1004 { 'V', "verbose", NULL,
1005 gettext_noop ("verbose output (include ATS address properties)"), 0,
1006 &GNUNET_GETOPT_set_one, &opt_verbose }, GNUNET_GETOPT_OPTION_END };
1008 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1011 res = GNUNET_PROGRAM_run (argc, argv, "gnunet-ats",
1012 gettext_noop ("Print information about ATS state"),
1015 GNUNET_free_non_null(opt_pid_str);
1016 GNUNET_free_non_null(opt_type_str);
1017 GNUNET_free((void *) argv);
1019 if (GNUNET_OK == res)
1026 /* end of gnunet-ats.c */