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"
34 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
39 #define BIG_M_STRING "unlimited"
54 static int resolve_addresses_numeric;
59 static int receive_done;
62 * For which peer should we change preference values?
69 static char *type_str;
74 static unsigned int value;
82 * Print verbose ATS information
87 * List only addresses currently used (active)
89 static int op_list_used;
94 static int op_list_all;
99 static int op_set_pref;
102 * Print quotas configured
104 static int op_print_quotas;
107 * Monitor addresses used
109 static int op_monitor;
114 static struct GNUNET_ATS_PerformanceHandle *ph;
119 static struct GNUNET_ATS_AddressListHandle *alh;
124 static struct GNUNET_CONFIGURATION_Handle *cfg;
129 static GNUNET_SCHEDULER_TaskIdentifier end_task;
134 static struct GNUNET_CONTAINER_MultiPeerMap *addresses;
138 * Structure used to remember all pending address resolutions.
139 * We keep address information in here while we talk to transport
140 * to map the address to a string.
142 struct PendingResolutions
147 struct PendingResolutions *next;
152 struct PendingResolutions *prev;
155 * Copy of the address we are resolving.
157 struct GNUNET_HELLO_Address *address;
160 * Handle to the transport request to convert the address
163 struct GNUNET_TRANSPORT_AddressToStringContext *tats_ctx;
166 * Array of performance data.
168 struct GNUNET_ATS_Information *ats;
171 * Length of the @e ats array.
176 * Amount of outbound bandwidth assigned by ATS.
178 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
181 * Amount of inbound bandwidth assigned by ATS.
183 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
186 * Is this an active address?
193 * Information we keep for an address. Used to avoid
194 * printing the same data multiple times.
199 * Address information.
201 struct GNUNET_HELLO_Address *address;
204 * Current outbound bandwidth.
206 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
209 * Current inbound bandwidth.
211 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
214 * Is this an active address?
222 * Head of list of pending resolution requests.
224 static struct PendingResolutions *head;
227 * Tail of list of pending resolution requests.
229 static struct PendingResolutions *tail;
233 * Free address corresponding to a given peer.
236 * @param key peer identity
237 * @param value the `struct ATSAddress *` to be freed
238 * @return #GNUNET_YES (always)
241 free_addr_it (void *cls,
242 const struct GNUNET_PeerIdentity *key,
245 struct ATSAddress *a = value;
247 GNUNET_break (GNUNET_OK ==
248 GNUNET_CONTAINER_multipeermap_remove (addresses,
251 GNUNET_HELLO_address_free (a->address);
258 * Task run on shutdown.
261 * @param tc scheduler context
265 const struct GNUNET_SCHEDULER_TaskContext *tc)
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 (op_list_used || op_list_all)
306 _("ATS returned results for %u addresses\n"),
313 * Function to call with a textual representation of an address. This
314 * function will be called several times with different possible
315 * textual representations, and a last time with @address being NULL
316 * to signal the end of the iteration. Note that @address NULL
317 * always is the last call, regardless of the value in @a res.
319 * @param cls closure, a `struct PendingResolutions *`
320 * @param address NULL on end of iteration,
321 * otherwise 0-terminated printable UTF-8 string,
322 * in particular an empty string if @a res is #GNUNET_NO
323 * @param res result of the address to string conversion:
324 * if #GNUNET_OK: conversion successful
325 * if #GNUNET_NO: address was invalid (or not supported)
326 * if #GNUNET_SYSERR: communication error (IPC error)
329 transport_addr_to_str_cb (void *cls,
333 struct PendingResolutions *pr = cls;
336 char *ats_prop_arr[GNUNET_ATS_PropertyCount] = GNUNET_ATS_PropertyStrings;
337 char *ats_prop_value;
346 GNUNET_CONTAINER_DLL_remove(head, tail, pr);
347 GNUNET_free(pr->address);
351 if ((GNUNET_YES == receive_done) && (0 == pending))
353 /* All messages received and no resolutions pending*/
354 if (end_task != GNUNET_SCHEDULER_NO_TASK)
355 GNUNET_SCHEDULER_cancel (end_task);
356 end_task = GNUNET_SCHEDULER_add_now (end, NULL);
364 "Failed to convert address for peer `%s' plugin `%s' length %lu to string (communication error)\n",
365 GNUNET_i2s (&pr->address->peer),
366 pr->address->transport_name,
367 pr->address->address_length);
371 "Failed to convert address for peer `%s' plugin `%s' length %lu to string (address invalid or not supported)\n",
372 GNUNET_i2s (&pr->address->peer),
373 pr->address->transport_name,
374 pr->address->address_length);
377 /* continues below */
384 ats_str = GNUNET_strdup (pr->active ? _("active ") : _("inactive "));
385 network = GNUNET_ATS_NET_UNSPECIFIED;
386 for (c = 0; c < pr->ats_count; c++)
390 ats_type = ntohl (pr->ats[c].type);
391 ats_value = ntohl (pr->ats[c].value);
393 if (ats_type > GNUNET_ATS_PropertyCount)
396 "Invalid ATS property type %u %u for address %s\n",
405 case GNUNET_ATS_NETWORK_TYPE:
406 if (ats_value > GNUNET_ATS_NetworkTypeCount)
412 GNUNET_asprintf (&ats_prop_value,
414 GNUNET_ATS_print_network_type (ats_value));
417 GNUNET_asprintf (&ats_prop_value, "%u", ats_value);
420 if ((verbose) && (ats_type < GNUNET_ATS_PropertyCount))
422 GNUNET_asprintf (&ats_str,
425 ats_prop_arr[ats_type],
427 GNUNET_free(ats_tmp);
429 GNUNET_free(ats_prop_value);
433 _("Peer `%s' plugin `%s', address `%s', `%s' bw out: %u Bytes/s, bw in %u Bytes/s, %s\n"),
434 GNUNET_i2s (&pr->address->peer),
435 pr->address->transport_name,
437 GNUNET_ATS_print_network_type (network),
438 ntohl (pr->bandwidth_out.value__),
439 ntohl (pr->bandwidth_in.value__),
441 GNUNET_free (ats_str);
446 * Closure for #find_address_it().
448 struct AddressFindCtx
451 * Address we are looking for.
453 const struct GNUNET_HELLO_Address *src;
456 * Where to write the `struct ATSAddress` if we found one that matches.
458 struct ATSAddress *res;
463 * Find address corresponding to a given peer.
465 * @param cls the `struct AddressFindCtx *`
466 * @param key peer identity
467 * @param value the `struct ATSAddress *` for an existing address
468 * @return #GNUNET_NO if we found a match, #GNUNET_YES if not
471 find_address_it (void *cls,
472 const struct GNUNET_PeerIdentity *key,
475 struct AddressFindCtx *actx = cls;
476 struct ATSAddress *exist = value;
478 if (0 == GNUNET_HELLO_address_cmp (actx->src, exist->address))
489 * Signature of a function that is called with QoS information about an address.
491 * @param cls closure (NULL)
492 * @param address the address, NULL if ATS service was disconnected
493 * @param active #GNUNET_YES if this address is actively used
494 * to maintain a connection to a peer;
495 * #GNUNET_NO if the address is not actively used;
496 * #GNUNET_SYSERR if this address is no longer available for ATS
497 * @param bandwidth_out assigned outbound bandwidth for the connection
498 * @param bandwidth_in assigned inbound bandwidth for the connection
499 * @param ats performance data for the address (as far as known)
500 * @param ats_count number of performance records in @a ats
503 ats_perf_mon_cb (void *cls,
504 const struct GNUNET_HELLO_Address *address,
506 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
507 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
508 const struct GNUNET_ATS_Information *ats,
511 struct PendingResolutions *pr;
512 struct PendingResolutions *cur;
513 struct PendingResolutions *next;
517 /* ATS service temporarily disconnected, remove current state */
519 for (cur = next; NULL != cur; cur = next)
522 GNUNET_CONTAINER_DLL_remove (head, tail, cur);
523 GNUNET_TRANSPORT_address_to_string_cancel (cur->tats_ctx);
524 GNUNET_HELLO_address_free (cur->address);
528 GNUNET_CONTAINER_multipeermap_iterate (addresses,
533 if (GNUNET_SYSERR == active)
536 struct AddressFindCtx actx;
540 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
544 if (NULL == actx.res)
549 GNUNET_break (GNUNET_OK ==
550 GNUNET_CONTAINER_multipeermap_remove (addresses,
554 _("Removed address of peer `%s' with plugin `%s'\n"),
555 GNUNET_i2s (&address->peer),
556 actx.res->address->transport_name);
557 GNUNET_HELLO_address_free (actx.res);
558 GNUNET_free (actx.res);
562 if (GNUNET_NO == verbose)
564 struct AddressFindCtx actx;
565 struct ATSAddress *a;
569 GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
573 if ((NULL != actx.res))
575 if ((bandwidth_in.value__ == actx.res->bandwidth_in.value__) &&
576 (bandwidth_out.value__ == actx.res->bandwidth_out.value__) &&
577 (active == actx.res->active))
579 return; /* Nothing to do here */
583 actx.res->bandwidth_in = bandwidth_in;
584 actx.res->bandwidth_out = bandwidth_out;
589 a = GNUNET_new (struct ATSAddress);
591 a->address = GNUNET_HELLO_address_copy(address);
592 a->bandwidth_in = bandwidth_in;
593 a->bandwidth_out = bandwidth_out;
595 GNUNET_CONTAINER_multipeermap_put (addresses,
598 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
602 pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
603 ats_count * sizeof (struct GNUNET_ATS_Information));
605 pr->ats_count = ats_count;
606 pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
608 memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
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 resolve_addresses_numeric,
615 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
616 &transport_addr_to_str_cb,
618 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
625 * Signature of a function that is called with QoS information about an address.
627 * @param cls closure (NULL)
628 * @param address the address, NULL if ATS service was disconnected
629 * @param active is this address actively used to maintain a connection
631 * @param bandwidth_out assigned outbound bandwidth for the connection
632 * @param bandwidth_in assigned inbound bandwidth for the connection
633 * @param ats performance data for the address (as far as known)
634 * @param ats_count number of performance records in @a ats
637 ats_perf_cb (void *cls,
638 const struct GNUNET_HELLO_Address *address,
640 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
641 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
642 const struct GNUNET_ATS_Information *ats,
645 struct PendingResolutions *pr;
649 /* All messages received */
650 receive_done = GNUNET_YES;
654 /* All messages received and no resolutions pending*/
655 if (end_task != GNUNET_SCHEDULER_NO_TASK)
656 GNUNET_SCHEDULER_cancel (end_task);
657 end_task = GNUNET_SCHEDULER_add_now (end, NULL);
662 pr = GNUNET_malloc (sizeof (struct PendingResolutions) +
663 ats_count * sizeof (struct GNUNET_ATS_Information));
665 pr->ats_count = ats_count;
666 pr->ats = (struct GNUNET_ATS_Information *) &pr[1];
668 memcpy (pr->ats, ats, ats_count * sizeof(struct GNUNET_ATS_Information));
669 pr->address = GNUNET_HELLO_address_copy (address);
670 pr->bandwidth_in = bandwidth_in;
671 pr->bandwidth_out = bandwidth_out;
673 pr->tats_ctx = GNUNET_TRANSPORT_address_to_string (cfg, address,
674 resolve_addresses_numeric,
675 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
676 &transport_addr_to_str_cb, pr);
677 GNUNET_CONTAINER_DLL_insert (head, tail, pr);
684 * Print information about the quotas configured for the various
687 * @param cfg configuration to obtain quota information from
688 * @return total number of ATS network types known
691 print_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg)
693 char *network_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
694 char * entry_in = NULL;
695 char * entry_out = NULL;
696 char * quota_out_str;
698 unsigned long long int quota_out;
699 unsigned long long int quota_in;
702 for (c = 0; (c < GNUNET_ATS_NetworkTypeCount); c++)
705 GNUNET_asprintf (&entry_out,
708 GNUNET_asprintf (&entry_in,
714 GNUNET_CONFIGURATION_get_value_string (cfg,
719 if (0 == strcmp (quota_out_str, BIG_M_STRING)
721 GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
723 quota_out = UINT32_MAX;
725 GNUNET_free(quota_out_str);
726 GNUNET_asprintf ("a_out_str, "%llu", quota_out);
731 "Outbound quota for network `%11s' not configured!\n",
733 GNUNET_asprintf ("a_out_str, "-");
735 GNUNET_free(entry_out);
739 GNUNET_CONFIGURATION_get_value_string (cfg,
744 if (0 == strcmp (quota_in_str, BIG_M_STRING)
746 GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
747 quota_in = UINT32_MAX;
748 GNUNET_free(quota_in_str);
749 GNUNET_asprintf ("a_in_str, "%llu", quota_in);
754 "Inbound quota for network `%11s' not configured!\n",
756 GNUNET_asprintf ("a_in_str, "-");
758 GNUNET_free(entry_in);
761 _("Quota for network `%11s' (in/out): %10s / %10s\n"),
765 GNUNET_free(quota_out_str);
766 GNUNET_free(quota_in_str);
768 return GNUNET_ATS_NetworkTypeCount;
773 * Function called with the result from the test if ATS is
774 * running. Runs the actual main logic.
776 * @param cls the `struct GNUNET_CONFIGURATION_Handle *`
777 * @param result result of the test, #GNUNET_YES if ATS is running
780 testservice_ats (void *cls,
783 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
784 struct GNUNET_PeerIdentity pid;
788 addresses = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_YES);
790 if (GNUNET_YES != result)
793 _("Service `%s' is not running\n"),
803 != GNUNET_CRYPTO_eddsa_public_key_from_string (pid_str,
804 strlen (pid_str), &pid.public_key))
807 _("Failed to parse peer identity `%s'\n"),
813 c = op_list_all + op_list_used + op_monitor + op_set_pref;
817 _("Please select one operation : %s or %s or %s or %s or %s\n"),
826 op_list_used = GNUNET_YES; /* set default */
829 ret = print_quotas (cfg);
834 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
839 _("Cannot connect to ATS service, exiting...\n"));
843 alh = GNUNET_ATS_performance_list_addresses (ph,
844 (NULL == pid_str) ? NULL : &pid,
851 _("Cannot issue request to ATS service, exiting...\n"));
852 end_task = GNUNET_SCHEDULER_add_now (&end, NULL);
855 end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
859 else if (op_list_used)
861 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
865 _("Cannot connect to ATS service, exiting...\n"));
867 alh = GNUNET_ATS_performance_list_addresses (ph,
877 _("Cannot issue request to ATS service, exiting...\n"));
878 end_task = GNUNET_SCHEDULER_add_now (&end, NULL);
881 end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
887 ph = GNUNET_ATS_performance_init (cfg,
893 _("Cannot connect to ATS service, exiting...\n"));
894 end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
899 else if (op_set_pref)
901 if (NULL == type_str)
905 _("No preference type given!\n"));
912 _("No peer given!\n"));
916 for (c = 0; c < strlen (type_str); c++)
918 if (isupper (type_str[c]))
919 type_str[c] = tolower (type_str[c]);
922 if (0 == strcasecmp ("latency", type_str))
923 type = GNUNET_ATS_PREFERENCE_LATENCY;
924 else if (0 == strcasecmp ("bandwidth", type_str))
925 type = GNUNET_ATS_PREFERENCE_BANDWIDTH;
930 _("Valid type required\n"));
935 ph = GNUNET_ATS_performance_init (cfg, NULL, NULL);
939 _("Cannot connect to ATS service, exiting...\n"));
941 GNUNET_ATS_performance_change_preference (ph, &pid, type, (double) value,
942 GNUNET_ATS_PREFERENCE_END);
944 end_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
953 * Main function that will be run by the scheduler.
956 * @param args remaining command-line arguments
957 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
958 * @param my_cfg configuration
964 const struct GNUNET_CONFIGURATION_Handle *my_cfg)
966 cfg = (struct GNUNET_CONFIGURATION_Handle *) my_cfg;
967 GNUNET_CLIENT_service_test ("ats", cfg, TIMEOUT,
976 * @param argc number of arguments from the command line
977 * @param argv command line arguments
978 * @return 0 ok, 1 on error
986 resolve_addresses_numeric = GNUNET_NO;
987 op_monitor = GNUNET_NO;
988 op_list_all = GNUNET_NO;
989 op_list_used = GNUNET_NO;
990 op_set_pref = GNUNET_NO;
992 receive_done = GNUNET_NO;
995 static const struct GNUNET_GETOPT_CommandLineOption options[] =
998 gettext_noop ("get list of active addresses currently used"), 0,
999 &GNUNET_GETOPT_set_one, &op_list_used },
1000 { 'a', "all", NULL, gettext_noop ("get list of all active addresses"), 0,
1001 &GNUNET_GETOPT_set_one, &op_list_all },
1002 { 'n', "numeric", NULL,
1003 gettext_noop ("do not resolve IP addresses to hostnames"), 0,
1004 &GNUNET_GETOPT_set_one, &resolve_addresses_numeric },
1005 { 'm', "monitor", NULL, gettext_noop ("monitor mode"), 0,
1006 &GNUNET_GETOPT_set_one, &op_monitor },
1007 { 'p', "preference", NULL, gettext_noop ("set preference for the given peer"),
1008 0, &GNUNET_GETOPT_set_one, &op_set_pref },
1009 { 'q', "quotas", NULL, gettext_noop ("print all configured quotas"), 0,
1010 &GNUNET_GETOPT_set_one, &op_print_quotas },
1011 { 'i', "id", "TYPE", gettext_noop ("peer id"), 1, &GNUNET_GETOPT_set_string,
1013 { 't', "type", "TYPE",
1014 gettext_noop ("preference type to set: latency | bandwidth"), 1,
1015 &GNUNET_GETOPT_set_string, &type_str },
1016 { 'k', "value", "VALUE", gettext_noop ("preference value"), 1,
1017 &GNUNET_GETOPT_set_uint, &value },
1018 { 'V', "verbose", NULL,
1019 gettext_noop ("verbose output (include ATS address properties)"), 0,
1020 &GNUNET_GETOPT_set_one, &verbose }, GNUNET_GETOPT_OPTION_END };
1022 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1025 res = GNUNET_PROGRAM_run (argc, argv, "gnunet-ats",
1026 gettext_noop ("Print information about ATS state"),
1029 GNUNET_free_non_null(pid_str);
1030 GNUNET_free_non_null(type_str);
1031 GNUNET_free((void *) argv);
1033 if (GNUNET_OK == res)
1040 /* end of gnunet-ats.c */