2 This file is part of GNUnet.
3 Copyright (C) 2010-2015 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU 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.
16 * @file ats/test_ats_lib.c
17 * @brief test ATS library with a generic interpreter for running ATS tests
18 * @author Christian Grothoff
21 #include "gnunet_util_lib.h"
22 #include "gnunet_ats_service.h"
23 #include "gnunet_testing_lib.h"
24 #include "test_ats_lib.h"
27 * Information about the last address suggestion we got for a peer.
29 struct AddressSuggestData
32 * Which session were we given?
34 struct GNUNET_ATS_Session *session;
37 * What address was assigned?
39 struct GNUNET_HELLO_Address *address;
42 * Outbound bandwidth assigned.
44 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
47 * Inbound bandwidth assigned.
49 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
52 * Was the bandwidth assigned non-zero?
59 * Information about the last address information we got for an address.
61 struct AddressInformationData
64 * What address is this data about?
66 struct GNUNET_HELLO_Address *address;
69 * Which properties were given?
71 struct GNUNET_ATS_Properties properties;
74 * Outbound bandwidth reported.
76 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
79 * Inbound bandwidth reported.
81 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
84 * Was the address said to be 'active'?
93 static struct GNUNET_ATS_SchedulingHandle *sched_ats;
98 static struct GNUNET_ATS_ConnectivityHandle *con_ats;
103 static struct GNUNET_ATS_PerformanceHandle *perf_ats;
106 * Handle for the interpreter task.
108 static struct GNUNET_SCHEDULER_Task *interpreter_task;
111 * Map from peer identities to the last address suggestion
112 * `struct AddressSuggestData` we got for the respective peer.
114 static struct GNUNET_CONTAINER_MultiPeerMap *p2asd;
117 * Map from peer identities to the last address information
118 * sets for all addresses of this peer. Each peer is mapped
119 * to one or more `struct AddressInformationData` entries.
121 static struct GNUNET_CONTAINER_MultiPeerMap *p2aid;
124 * Global timeout for the test.
126 static struct GNUNET_TIME_Relative TIMEOUT;
129 * Return value from #main().
134 * Current global command offset into the #commands array.
136 static unsigned int off;
139 * Commands for the current test.
141 static struct Command *test_commands;
146 * Free `struct AddressSuggestData` entry.
150 * @param value the `struct AddressSuggestData` to release
151 * @return #GNUNET_OK (continue to iterate)
155 const struct GNUNET_PeerIdentity *key,
158 struct AddressSuggestData *asd = value;
160 GNUNET_assert (GNUNET_YES ==
161 GNUNET_CONTAINER_multipeermap_remove (p2asd,
164 GNUNET_free_non_null (asd->address);
171 * Free `struct AddressInformationData` entry.
175 * @param value the `struct AddressSuggestData` to release
176 * @return #GNUNET_OK (continue to iterate)
180 const struct GNUNET_PeerIdentity *key,
183 struct AddressInformationData *aid = value;
185 GNUNET_assert (GNUNET_YES ==
186 GNUNET_CONTAINER_multipeermap_remove (p2aid,
189 GNUNET_free (aid->address);
196 * Find latest address suggestion made for the given peer.
198 * @param pid peer to look up
199 * @return NULL if peer was never involved
201 static struct AddressSuggestData *
202 find_address_suggestion (const struct GNUNET_PeerIdentity *pid)
204 return GNUNET_CONTAINER_multipeermap_get (p2asd,
210 * Closure for #match_address()
212 struct MatchAddressContext
217 const struct GNUNET_HELLO_Address *addr;
220 * Where to return address information if found.
222 struct AddressInformationData *ret;
227 * Find matching address information.
229 * @param cls a `struct MatchAddressContext`
231 * @param value a `struct AddressInformationData`
232 * @return #GNUNET_OK if not found
235 match_address (void *cls,
236 const struct GNUNET_PeerIdentity *key,
239 struct MatchAddressContext *mac = cls;
240 struct AddressInformationData *aid = value;
242 if (0 == GNUNET_HELLO_address_cmp (mac->addr,
253 * Find latest address information made for the given address.
255 * @param addr address to look up
256 * @return NULL if peer was never involved
258 static struct AddressInformationData *
259 find_address_information (const struct GNUNET_HELLO_Address *addr)
261 struct MatchAddressContext mac;
265 GNUNET_CONTAINER_multipeermap_get_multiple (p2aid,
274 * Task run to terminate the testcase.
282 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
283 "Test failed at stage %u %s\n",
285 (NULL != test_commands[off].label)
286 ? test_commands[off].label
288 if (NULL != interpreter_task)
290 GNUNET_SCHEDULER_cancel (interpreter_task);
291 interpreter_task = NULL;
293 if (NULL != sched_ats)
295 GNUNET_ATS_scheduling_done (sched_ats);
300 GNUNET_ATS_connectivity_done (con_ats);
303 if (NULL != perf_ats)
305 GNUNET_ATS_performance_done (perf_ats);
310 GNUNET_CONTAINER_multipeermap_iterate (p2asd,
313 GNUNET_CONTAINER_multipeermap_destroy (p2asd);
318 GNUNET_CONTAINER_multipeermap_iterate (p2aid,
321 GNUNET_CONTAINER_multipeermap_destroy (p2aid);
328 * Main interpreter loop. Runs the steps of the test.
333 interpreter (void *cls);
337 * Run the interpreter next.
342 if (NULL != interpreter_task)
343 GNUNET_SCHEDULER_cancel (interpreter_task);
344 interpreter_task = GNUNET_SCHEDULER_add_now (&interpreter,
350 * Initialize public key of a peer based on a single number.
352 * @param pid number to use as the basis
353 * @param pk resulting fake public key
356 make_peer (uint32_t pid,
357 struct GNUNET_PeerIdentity *pk)
361 sizeof (struct GNUNET_PeerIdentity));
369 * Generate a fake address based on the given parameters.
371 * @param pid number of the peer
372 * @param num number of the address at peer @a pid
373 * @param addr_flags flags to use for the address
374 * @return the address
376 static struct GNUNET_HELLO_Address *
377 make_address (uint32_t pid,
379 enum GNUNET_HELLO_AddressInfo addr_flags)
381 struct GNUNET_PeerIdentity pk;
387 return GNUNET_HELLO_address_allocate (&pk,
396 * Our dummy sessions.
398 struct GNUNET_ATS_Session
401 * Field to avoid `0 == sizeof(struct GNUNET_ATS_Session)`.
403 unsigned int non_empty;
408 * Create a session instance for ATS.
410 * @param i which session number to return
411 * @return NULL if @a i is 0, otherwise a pointer unique to @a i
413 static struct GNUNET_ATS_Session *
414 make_session (unsigned int i)
416 struct GNUNET_ATS_Session *baseptr = NULL;
420 /* Yes, these are *intentionally* out-of-bounds,
421 and offset from NULL, as nobody should ever
422 use those other than to compare pointers! */
428 * Find a @a code command before the global #off with the
429 * specified @a label.
431 * @param code opcode to look for
432 * @param label label to look for, NULL for none
433 * @return previous command with the matching label
435 static struct Command *
436 find_command (enum CommandCode code,
443 for (i=off-1;i>=0;i--)
444 if ( (code == test_commands[i].code) &&
445 (0 == strcmp (test_commands[i].label,
447 return &test_commands[i];
454 * Function called from #GNUNET_ATS_performance_list_addresses when
455 * we process a #CMD_LIST_ADDRESSES command.
457 * @param cls the `struct Command` that caused the call
458 * @param address the address, NULL if ATS service was disconnected
459 * @param address_active #GNUNET_YES if this address is actively used
460 * to maintain a connection to a peer;
461 * #GNUNET_NO if the address is not actively used;
462 * #GNUNET_SYSERR if this address is no longer available for ATS
463 * @param bandwidth_out assigned outbound bandwidth for the connection
464 * @param bandwidth_in assigned inbound bandwidth for the connection
465 * @param prop performance data for the address
469 const struct GNUNET_HELLO_Address *address,
471 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
472 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
473 const struct GNUNET_ATS_Properties *prop)
475 struct Command *c = cls;
476 struct CommandListAddresses *cmd = &c->details.list_addresses;
481 /* we are done with the iteration, continue to execute */
482 if ( (cmd->calls < cmd->min_calls) &&
483 (cmd->active_calls < cmd->min_active_calls) )
485 GNUNET_SCHEDULER_shutdown ();
492 switch (address_active)
504 if ( (cmd->calls > cmd->max_calls) &&
505 (cmd->active_calls < cmd->max_active_calls) )
508 GNUNET_ATS_performance_list_addresses_cancel (cmd->alh);
510 GNUNET_SCHEDULER_shutdown ();
517 * Function called with reservation result.
519 * @param cls closure with the reservation command (`struct Command`)
520 * @param peer identifies the peer
521 * @param amount set to the amount that was actually reserved or unreserved;
522 * either the full requested amount or zero (no partial reservations)
523 * @param res_delay if the reservation could not be satisfied (amount was 0), how
524 * long should the client wait until re-trying?
527 reservation_cb (void *cls,
528 const struct GNUNET_PeerIdentity *peer,
530 struct GNUNET_TIME_Relative res_delay)
532 struct Command *cmd = cls;
533 struct GNUNET_PeerIdentity pid;
535 cmd->details.reserve_bandwidth.rc = NULL;
536 make_peer (cmd->details.reserve_bandwidth.pid,
538 GNUNET_assert (0 == memcmp (peer,
540 sizeof (struct GNUNET_PeerIdentity)));
541 switch (cmd->details.reserve_bandwidth.expected_result)
544 if (amount != cmd->details.reserve_bandwidth.amount)
546 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
547 "Unexpectedly failed to reserve %d/%d bytes with delay %s!\n",
549 (int) cmd->details.reserve_bandwidth.amount,
550 GNUNET_STRINGS_relative_time_to_string (res_delay,
553 GNUNET_SCHEDULER_shutdown ();
558 GNUNET_break ( (0 != amount) ||
559 (0 != res_delay.rel_value_us) );
562 if ( (amount != 0) ||
563 (0 == res_delay.rel_value_us) )
565 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566 "Unexpectedly reserved %d bytes with delay %s!\n",
568 GNUNET_STRINGS_relative_time_to_string (res_delay,
571 GNUNET_SCHEDULER_shutdown ();
582 * Main interpreter loop. Runs the steps of the test.
587 interpreter (void *cls)
592 interpreter_task = NULL;
595 cmd = &test_commands[off];
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600 (NULL != cmd->label) ? cmd->label : "");
605 GNUNET_SCHEDULER_shutdown ();
607 case CMD_ADD_ADDRESS:
609 struct GNUNET_HELLO_Address *addr;
610 struct GNUNET_ATS_Session *session;
612 addr = make_address (cmd->details.add_address.pid,
613 cmd->details.add_address.addr_num,
614 cmd->details.add_address.addr_flags);
615 session = make_session (cmd->details.add_address.session);
616 if (cmd->details.add_address.expect_fail)
617 GNUNET_log_skip (1, GNUNET_NO);
618 cmd->details.add_address.ar
619 = GNUNET_ATS_address_add (sched_ats,
622 &cmd->details.add_address.properties);
624 if (cmd->details.add_address.expect_fail)
626 GNUNET_log_skip (0, GNUNET_YES);
628 else if (NULL == cmd->details.add_address.ar)
631 GNUNET_SCHEDULER_shutdown ();
637 case CMD_DEL_ADDRESS:
641 add = find_command (CMD_ADD_ADDRESS,
642 cmd->details.del_address.add_label);
643 GNUNET_assert (NULL != add->details.add_address.ar);
644 GNUNET_ATS_address_destroy (add->details.add_address.ar);
645 add->details.add_address.ar = NULL;
649 case CMD_AWAIT_ADDRESS_SUGGESTION:
651 struct GNUNET_PeerIdentity pid;
652 struct GNUNET_HELLO_Address *addr;
654 struct AddressSuggestData *asd;
657 make_peer (cmd->details.await_address_suggestion.pid,
659 asd = find_address_suggestion (&pid);
662 if (GNUNET_NO == asd->active)
663 return; /* last suggestion was to disconnect, wait longer */
665 if (NULL != cmd->details.await_address_suggestion.add_label)
668 add = find_command (CMD_ADD_ADDRESS,
669 cmd->details.await_address_suggestion.add_label);
670 addr = make_address (add->details.add_address.pid,
671 add->details.add_address.addr_num,
672 add->details.add_address.addr_flags);
673 if ( (asd->session ==
674 make_session (add->details.add_address.session)) &&
676 GNUNET_HELLO_address_cmp (addr,
681 if (GNUNET_NO == done)
686 case CMD_AWAIT_DISCONNECT_SUGGESTION:
688 struct GNUNET_PeerIdentity pid;
689 struct AddressSuggestData *asd;
691 make_peer (cmd->details.await_disconnect_suggestion.pid,
693 asd = find_address_suggestion (&pid);
695 return; /* odd, no suggestion at all yet!? */
696 if (GNUNET_YES == asd->active)
697 return; /* last suggestion was to activate, wait longer */
698 /* last suggestion was to deactivate, condition satisfied! */
702 case CMD_REQUEST_CONNECTION_START:
704 struct GNUNET_PeerIdentity pid;
706 make_peer (cmd->details.request_connection_start.pid,
708 cmd->details.request_connection_start.csh
709 = GNUNET_ATS_connectivity_suggest (con_ats,
715 case CMD_REQUEST_CONNECTION_STOP:
717 struct Command *start;
719 start = find_command (CMD_REQUEST_CONNECTION_START,
720 cmd->details.request_connection_stop.connect_label);
721 GNUNET_ATS_connectivity_suggest_cancel (start->details.request_connection_start.csh);
722 start->details.request_connection_start.csh = NULL;
726 case CMD_AWAIT_ADDRESS_INFORMATION:
728 struct AddressInformationData *aid;
730 struct Command *update;
731 struct GNUNET_HELLO_Address *addr;
732 const struct GNUNET_ATS_Properties *cmp;
734 add = find_command (CMD_ADD_ADDRESS,
735 cmd->details.await_address_information.add_label);
736 update = find_command (CMD_UPDATE_ADDRESS,
737 cmd->details.await_address_information.update_label);
738 addr = make_address (add->details.add_address.pid,
739 add->details.add_address.addr_num,
740 add->details.add_address.addr_flags);
741 aid = find_address_information (addr);
744 cmp = &add->details.add_address.properties;
746 cmp = &update->details.update_address.properties;
747 if ( (NULL != aid) &&
748 (cmp->delay.rel_value_us == aid->properties.delay.rel_value_us) &&
749 (cmp->utilization_out == aid->properties.utilization_out) &&
750 (cmp->utilization_in == aid->properties.utilization_in) &&
751 (cmp->distance == aid->properties.distance) &&
752 (cmp->scope == aid->properties.scope) )
759 case CMD_UPDATE_ADDRESS:
763 add = find_command (CMD_ADD_ADDRESS,
764 cmd->details.update_address.add_label);
765 GNUNET_assert (NULL != add->details.add_address.ar);
766 GNUNET_ATS_address_update (add->details.add_address.ar,
767 &cmd->details.update_address.properties);
771 case CMD_ADD_SESSION:
774 struct GNUNET_ATS_Session *session;
776 add = find_command (CMD_ADD_ADDRESS,
777 cmd->details.add_session.add_label);
778 session = make_session (cmd->details.add_session.session);
779 GNUNET_assert (NULL != add->details.add_address.ar);
780 GNUNET_ATS_address_add_session (add->details.add_address.ar,
785 case CMD_DEL_SESSION:
787 struct Command *add_address;
788 struct Command *add_session;
789 struct GNUNET_ATS_Session *session;
791 add_session = find_command (CMD_ADD_SESSION,
792 cmd->details.del_session.add_session_label);
793 add_address = find_command (CMD_ADD_ADDRESS,
794 add_session->details.add_session.add_label);
795 GNUNET_assert (NULL != add_address->details.add_address.ar);
796 session = make_session (add_session->details.add_session.session);
797 GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
802 case CMD_CHANGE_PREFERENCE:
804 struct GNUNET_PeerIdentity pid;
806 make_peer (cmd->details.change_preference.pid,
808 GNUNET_ATS_performance_change_preference (perf_ats,
810 GNUNET_ATS_PREFERENCE_END);
814 case CMD_PROVIDE_FEEDBACK:
816 struct GNUNET_PeerIdentity pid;
818 make_peer (cmd->details.provide_feedback.pid,
820 GNUNET_ATS_performance_give_feedback (perf_ats,
822 cmd->details.provide_feedback.scope,
823 GNUNET_ATS_PREFERENCE_END);
827 case CMD_LIST_ADDRESSES:
829 struct GNUNET_PeerIdentity pid;
831 make_peer (cmd->details.list_addresses.pid,
833 cmd->details.list_addresses.alh
834 = GNUNET_ATS_performance_list_addresses (perf_ats,
836 cmd->details.list_addresses.all,
841 case CMD_RESERVE_BANDWIDTH:
843 struct GNUNET_PeerIdentity pid;
845 make_peer (cmd->details.reserve_bandwidth.pid,
847 cmd->details.reserve_bandwidth.rc
848 = GNUNET_ATS_reserve_bandwidth (perf_ats,
850 cmd->details.reserve_bandwidth.amount,
857 interpreter_task = GNUNET_SCHEDULER_add_delayed (cmd->details.sleep.delay,
867 * Signature of a function called by ATS with the current bandwidth
868 * and address preferences as determined by ATS.
870 * @param cls closure, should point to "asc-closure"
871 * @param peer for which we suggest an address, NULL if ATS connection died
872 * @param address suggested address (including peer identity of the peer),
873 * may be NULL to signal disconnect from peer
874 * @param session session to use, NULL to establish a new outgoing session
875 * @param bandwidth_out assigned outbound bandwidth for the connection,
876 * 0 to signal disconnect
877 * @param bandwidth_in assigned inbound bandwidth for the connection,
878 * 0 to signal disconnect
881 address_suggest_cb (void *cls,
882 const struct GNUNET_PeerIdentity *peer,
883 const struct GNUNET_HELLO_Address *address,
884 struct GNUNET_ATS_Session *session,
885 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
886 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
888 const char *asc_cls = cls;
889 struct AddressSuggestData *asd;
891 GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
894 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
895 "Connection to ATS died, likely a crash!\n");
896 GNUNET_SCHEDULER_shutdown ();
898 /* This is what we should do if we wanted to continue past
900 GNUNET_CONTAINER_multipeermap_iterate (p2asd,
903 GNUNET_CONTAINER_multipeermap_iterate (p2aid,
910 asd = find_address_suggestion (peer);
913 asd = GNUNET_new (struct AddressSuggestData);
914 GNUNET_assert (GNUNET_YES ==
915 GNUNET_CONTAINER_multipeermap_put (p2asd,
918 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
920 if ( (0 == ntohl (bandwidth_out.value__)) &&
921 (0 == ntohl (bandwidth_in.value__)) )
922 asd->active = GNUNET_NO;
924 asd->active = GNUNET_YES;
925 asd->bandwidth_out = bandwidth_out;
926 asd->bandwidth_in = bandwidth_in;
927 asd->session = session;
928 GNUNET_free_non_null (asd->address);
931 asd->address = GNUNET_HELLO_address_copy (address);
932 if (NULL == interpreter_task)
938 * Signature of a function that is called with QoS information about an address.
940 * @param cls closure, should point to "aic-closure"
941 * @param address the address, NULL if ATS service was disconnected
942 * @param address_active #GNUNET_YES if this address is actively used
943 * to maintain a connection to a peer;
944 * #GNUNET_NO if the address is not actively used;
945 * #GNUNET_SYSERR if this address is no longer available for ATS
946 * @param bandwidth_out assigned outbound bandwidth for the connection
947 * @param bandwidth_in assigned inbound bandwidth for the connection
948 * @param prop performance data for the address
951 address_information_cb (void *cls,
952 const struct GNUNET_HELLO_Address *address,
954 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
955 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
956 const struct GNUNET_ATS_Properties *prop)
958 const char *aic_cls = cls;
959 struct AddressInformationData *aid;
961 GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
964 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
965 "Connection to ATS died, likely a crash!\n");
966 GNUNET_CONTAINER_multipeermap_iterate (p2aid,
972 aid = find_address_information (address);
975 aid = GNUNET_new (struct AddressInformationData);
976 aid->address = GNUNET_HELLO_address_copy (address);
977 GNUNET_assert (GNUNET_YES ==
978 GNUNET_CONTAINER_multipeermap_put (p2aid,
981 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
983 aid->active = address_active;
984 aid->bandwidth_out = bandwidth_out;
985 aid->bandwidth_in = bandwidth_in;
986 aid->properties = *prop;
987 if (NULL == interpreter_task)
993 * Function run once the ATS service has been started.
996 * @param cfg configuration for the testcase
997 * @param peer handle to the peer
1001 const struct GNUNET_CONFIGURATION_Handle *cfg,
1002 struct GNUNET_TESTING_Peer *peer)
1004 p2asd = GNUNET_CONTAINER_multipeermap_create (128,
1006 p2aid = GNUNET_CONTAINER_multipeermap_create (128,
1008 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
1012 sched_ats = GNUNET_ATS_scheduling_init (cfg,
1013 &address_suggest_cb,
1015 if (NULL == sched_ats)
1018 GNUNET_SCHEDULER_shutdown ();
1021 con_ats = GNUNET_ATS_connectivity_init (cfg);
1022 if (NULL == con_ats)
1025 GNUNET_SCHEDULER_shutdown ();
1028 perf_ats = GNUNET_ATS_performance_init (cfg,
1029 &address_information_cb,
1031 if (NULL == perf_ats)
1034 GNUNET_SCHEDULER_shutdown ();
1044 * @param argc length of @a argv
1045 * @param argv command line
1046 * @param cmds commands to run with the interpreter
1047 * @param timeout how long is the test allowed to take?
1048 * @return 0 on success
1051 TEST_ATS_run (int argc,
1053 struct Command *cmds,
1054 struct GNUNET_TIME_Relative timeout)
1056 char *test_filename = GNUNET_strdup (argv[0]);
1061 test_commands = cmds;
1063 if (NULL != (sep = strstr (test_filename, ".exe")))
1065 underscore = strrchr (test_filename, (int) '_');
1066 GNUNET_assert (NULL != underscore);
1067 GNUNET_asprintf (&config_file,
1068 "test_ats_api_%s.conf",
1071 if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
1075 GNUNET_free (test_filename);
1076 GNUNET_free (config_file);
1080 /* end of test_ats_lib.c */