global reindent, now with uncrustify hook enabled
[oweals/gnunet.git] / src / ats / test_ats_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file ats/test_ats_lib.c
22  * @brief test ATS library with a generic interpreter for running ATS tests
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet_testing_lib.h"
29 #include "test_ats_lib.h"
30
31 /**
32  * Information about the last address suggestion we got for a peer.
33  */
34 struct AddressSuggestData
35 {
36   /**
37    * Which session were we given?
38    */
39   struct GNUNET_ATS_Session *session;
40
41   /**
42    * What address was assigned?
43    */
44   struct GNUNET_HELLO_Address *address;
45
46   /**
47    * Outbound bandwidth assigned.
48    */
49   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
50
51   /**
52    * Inbound bandwidth assigned.
53    */
54   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
55
56   /**
57    * Was the bandwidth assigned non-zero?
58    */
59   int active;
60 };
61
62
63 /**
64  * Information about the last address information we got for an address.
65  */
66 struct AddressInformationData
67 {
68   /**
69    * What address is this data about?
70    */
71   struct GNUNET_HELLO_Address *address;
72
73   /**
74    * Which properties were given?
75    */
76   struct GNUNET_ATS_Properties properties;
77
78   /**
79    * Outbound bandwidth reported.
80    */
81   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
82
83   /**
84    * Inbound bandwidth reported.
85    */
86   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
87
88   /**
89    * Was the address said to be 'active'?
90    */
91   int active;
92 };
93
94
95 /**
96  * Scheduling handle
97  */
98 static struct GNUNET_ATS_SchedulingHandle *sched_ats;
99
100 /**
101  * Connectivity handle
102  */
103 static struct GNUNET_ATS_ConnectivityHandle *con_ats;
104
105 /**
106  * Performance handle
107  */
108 static struct GNUNET_ATS_PerformanceHandle *perf_ats;
109
110 /**
111  * Handle for the interpreter task.
112  */
113 static struct GNUNET_SCHEDULER_Task *interpreter_task;
114
115 /**
116  * Map from peer identities to the last address suggestion
117  * `struct AddressSuggestData` we got for the respective peer.
118  */
119 static struct GNUNET_CONTAINER_MultiPeerMap *p2asd;
120
121 /**
122  * Map from peer identities to the last address information
123  * sets for all addresses of this peer. Each peer is mapped
124  * to one or more `struct AddressInformationData` entries.
125  */
126 static struct GNUNET_CONTAINER_MultiPeerMap *p2aid;
127
128 /**
129  * Global timeout for the test.
130  */
131 static struct GNUNET_TIME_Relative TIMEOUT;
132
133 /**
134  * Return value from #main().
135  */
136 static int ret;
137
138 /**
139  * Current global command offset into the #commands array.
140  */
141 static unsigned int off;
142
143 /**
144  * Commands for the current test.
145  */
146 static struct Command *test_commands;
147
148
149
150 /**
151  * Free `struct AddressSuggestData` entry.
152  *
153  * @param cls NULL
154  * @param key ignored
155  * @param value the `struct AddressSuggestData` to release
156  * @return #GNUNET_OK (continue to iterate)
157  */
158 static int
159 free_asd (void *cls,
160           const struct GNUNET_PeerIdentity *key,
161           void *value)
162 {
163   struct AddressSuggestData *asd = value;
164
165   GNUNET_assert (GNUNET_YES ==
166                  GNUNET_CONTAINER_multipeermap_remove (p2asd,
167                                                        key,
168                                                        asd));
169   GNUNET_free_non_null (asd->address);
170   GNUNET_free (asd);
171   return GNUNET_OK;
172 }
173
174
175 /**
176  * Free `struct AddressInformationData` entry.
177  *
178  * @param cls NULL
179  * @param key ignored
180  * @param value the `struct AddressSuggestData` to release
181  * @return #GNUNET_OK (continue to iterate)
182  */
183 static int
184 free_aid (void *cls,
185           const struct GNUNET_PeerIdentity *key,
186           void *value)
187 {
188   struct AddressInformationData *aid = value;
189
190   GNUNET_assert (GNUNET_YES ==
191                  GNUNET_CONTAINER_multipeermap_remove (p2aid,
192                                                        key,
193                                                        aid));
194   GNUNET_free (aid->address);
195   GNUNET_free (aid);
196   return GNUNET_OK;
197 }
198
199
200 /**
201  * Find latest address suggestion made for the given peer.
202  *
203  * @param pid peer to look up
204  * @return NULL if peer was never involved
205  */
206 static struct AddressSuggestData *
207 find_address_suggestion (const struct GNUNET_PeerIdentity *pid)
208 {
209   return GNUNET_CONTAINER_multipeermap_get (p2asd,
210                                             pid);
211 }
212
213
214 /**
215  * Closure for #match_address()
216  */
217 struct MatchAddressContext
218 {
219   /**
220    * Address to find.
221    */
222   const struct GNUNET_HELLO_Address *addr;
223
224   /**
225    * Where to return address information if found.
226    */
227   struct AddressInformationData *ret;
228 };
229
230
231 /**
232  * Find matching address information.
233  *
234  * @param cls a `struct MatchAddressContext`
235  * @param key unused
236  * @param value a `struct AddressInformationData`
237  * @return #GNUNET_OK if not found
238  */
239 static int
240 match_address (void *cls,
241                const struct GNUNET_PeerIdentity *key,
242                void *value)
243 {
244   struct MatchAddressContext *mac = cls;
245   struct AddressInformationData *aid = value;
246
247   if (0 == GNUNET_HELLO_address_cmp (mac->addr,
248                                      aid->address))
249   {
250     mac->ret = aid;
251     return GNUNET_NO;
252   }
253   return GNUNET_OK;
254 }
255
256
257 /**
258  * Find latest address information made for the given address.
259  *
260  * @param addr address to look up
261  * @return NULL if peer was never involved
262  */
263 static struct AddressInformationData *
264 find_address_information (const struct GNUNET_HELLO_Address *addr)
265 {
266   struct MatchAddressContext mac;
267
268   mac.ret = NULL;
269   mac.addr = addr;
270   GNUNET_CONTAINER_multipeermap_get_multiple (p2aid,
271                                               &addr->peer,
272                                               &match_address,
273                                               &mac);
274   return mac.ret;
275 }
276
277
278 /**
279  * Task run to terminate the testcase.
280  *
281  * @param cls NULL
282  */
283 static void
284 end (void *cls)
285 {
286   if (0 != ret)
287     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288                 "Test failed at stage %u %s\n",
289                 off,
290                 (NULL != test_commands[off].label)
291                 ? test_commands[off].label
292                 : "");
293   if (NULL != interpreter_task)
294   {
295     GNUNET_SCHEDULER_cancel (interpreter_task);
296     interpreter_task = NULL;
297   }
298   if (NULL != sched_ats)
299   {
300     GNUNET_ATS_scheduling_done (sched_ats);
301     sched_ats = NULL;
302   }
303   if (NULL != con_ats)
304   {
305     GNUNET_ATS_connectivity_done (con_ats);
306     con_ats = NULL;
307   }
308   if (NULL != perf_ats)
309   {
310     GNUNET_ATS_performance_done (perf_ats);
311     perf_ats = NULL;
312   }
313   if (NULL != p2asd)
314   {
315     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
316                                            &free_asd,
317                                            NULL);
318     GNUNET_CONTAINER_multipeermap_destroy (p2asd);
319     p2asd = NULL;
320   }
321   if (NULL != p2aid)
322   {
323     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
324                                            &free_aid,
325                                            NULL);
326     GNUNET_CONTAINER_multipeermap_destroy (p2aid);
327     p2aid = NULL;
328   }
329 }
330
331
332 /**
333  * Main interpreter loop. Runs the steps of the test.
334  *
335  * @param cls NULL
336  */
337 static void
338 interpreter (void *cls);
339
340
341 /**
342  * Run the interpreter next.
343  */
344 static void
345 run_interpreter ()
346 {
347   if (NULL != interpreter_task)
348     GNUNET_SCHEDULER_cancel (interpreter_task);
349   interpreter_task = GNUNET_SCHEDULER_add_now (&interpreter,
350                                                NULL);
351 }
352
353
354 /**
355  * Initialize public key of a peer based on a single number.
356  *
357  * @param pid number to use as the basis
358  * @param pk resulting fake public key
359  */
360 static void
361 make_peer (uint32_t pid,
362            struct GNUNET_PeerIdentity *pk)
363 {
364   memset (pk,
365           (int) pid,
366           sizeof(struct GNUNET_PeerIdentity));
367   GNUNET_memcpy (pk,
368                  &pid,
369                  sizeof(uint32_t));
370 }
371
372
373 /**
374  * Generate a fake address based on the given parameters.
375  *
376  * @param pid number of the peer
377  * @param num number of the address at peer @a pid
378  * @param addr_flags flags to use for the address
379  * @return the address
380  */
381 static struct GNUNET_HELLO_Address *
382 make_address (uint32_t pid,
383               uint32_t num,
384               enum GNUNET_HELLO_AddressInfo addr_flags)
385 {
386   struct GNUNET_PeerIdentity pk;
387   uint32_t nbo;
388
389   nbo = htonl (num);
390   make_peer (pid,
391              &pk);
392   return GNUNET_HELLO_address_allocate (&pk,
393                                         "test",
394                                         &nbo,
395                                         sizeof(nbo),
396                                         addr_flags);
397 }
398
399
400 /**
401  * Our dummy sessions.
402  */
403 struct GNUNET_ATS_Session
404 {
405   /**
406    * Field to avoid `0 == sizeof(struct GNUNET_ATS_Session)`.
407    */
408   unsigned int non_empty;
409 };
410
411
412 /**
413  * Create a session instance for ATS.
414  *
415  * @param i which session number to return
416  * @return NULL if @a i is 0, otherwise a pointer unique to @a i
417  */
418 static struct GNUNET_ATS_Session *
419 make_session (unsigned int i)
420 {
421   struct GNUNET_ATS_Session *baseptr = NULL;
422
423   if (0 == i)
424     return NULL;
425   /* Yes, these are *intentionally* out-of-bounds,
426      and offset from NULL, as nobody should ever
427      use those other than to compare pointers! */
428   return baseptr + i;
429 }
430
431
432 /**
433  * Find a @a code command before the global #off with the
434  * specified @a label.
435  *
436  * @param code opcode to look for
437  * @param label label to look for, NULL for none
438  * @return previous command with the matching label
439  */
440 static struct Command *
441 find_command (enum CommandCode code,
442               const char *label)
443 {
444   int i;
445
446   if (NULL == label)
447     return NULL;
448   for (i = off - 1; i >= 0; i--)
449     if ((code == test_commands[i].code) &&
450         (0 == strcmp (test_commands[i].label,
451                       label)))
452       return &test_commands[i];
453   GNUNET_break (0);
454   return NULL;
455 }
456
457
458 /**
459  * Function called from #GNUNET_ATS_performance_list_addresses when
460  * we process a #CMD_LIST_ADDRESSES command.
461  *
462  * @param cls the `struct Command` that caused the call
463  * @param address the address, NULL if ATS service was disconnected
464  * @param address_active #GNUNET_YES if this address is actively used
465  *        to maintain a connection to a peer;
466  *        #GNUNET_NO if the address is not actively used;
467  *        #GNUNET_SYSERR if this address is no longer available for ATS
468  * @param bandwidth_out assigned outbound bandwidth for the connection
469  * @param bandwidth_in assigned inbound bandwidth for the connection
470  * @param prop performance data for the address
471  */
472 static void
473 info_cb (void *cls,
474          const struct GNUNET_HELLO_Address *address,
475          int address_active,
476          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
477          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
478          const struct GNUNET_ATS_Properties *prop)
479 {
480   struct Command *c = cls;
481   struct CommandListAddresses *cmd = &c->details.list_addresses;
482
483   if (NULL == address)
484   {
485     cmd->alh = NULL;
486     /* we are done with the iteration, continue to execute */
487     if ((cmd->calls < cmd->min_calls) &&
488         (cmd->active_calls < cmd->min_active_calls))
489     {
490       GNUNET_SCHEDULER_shutdown ();
491       return;
492     }
493     off++;
494     run_interpreter ();
495     return;
496   }
497   switch (address_active)
498   {
499   case GNUNET_YES:
500     cmd->active_calls++;
501     cmd->calls++;
502     break;
503
504   case GNUNET_NO:
505     cmd->calls++;
506     break;
507
508   case GNUNET_SYSERR:
509     return;
510   }
511   if ((cmd->calls > cmd->max_calls) &&
512       (cmd->active_calls < cmd->max_active_calls))
513   {
514     GNUNET_break (0);
515     GNUNET_ATS_performance_list_addresses_cancel (cmd->alh);
516     cmd->alh = NULL;
517     GNUNET_SCHEDULER_shutdown ();
518     return;
519   }
520 }
521
522
523 /**
524  * Function called with reservation result.
525  *
526  * @param cls closure with the reservation command (`struct Command`)
527  * @param peer identifies the peer
528  * @param amount set to the amount that was actually reserved or unreserved;
529  *               either the full requested amount or zero (no partial reservations)
530  * @param res_delay if the reservation could not be satisfied (amount was 0), how
531  *        long should the client wait until re-trying?
532  */
533 static void
534 reservation_cb (void *cls,
535                 const struct GNUNET_PeerIdentity *peer,
536                 int32_t amount,
537                 struct GNUNET_TIME_Relative res_delay)
538 {
539   struct Command *cmd = cls;
540   struct GNUNET_PeerIdentity pid;
541
542   cmd->details.reserve_bandwidth.rc = NULL;
543   make_peer (cmd->details.reserve_bandwidth.pid,
544              &pid);
545   GNUNET_assert (0 == GNUNET_memcmp (peer,
546                                      &pid));
547   switch (cmd->details.reserve_bandwidth.expected_result)
548   {
549   case GNUNET_OK:
550     if (amount != cmd->details.reserve_bandwidth.amount)
551     {
552       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
553                   "Unexpectedly failed to reserve %d/%d bytes with delay %s!\n",
554                   (int) amount,
555                   (int) cmd->details.reserve_bandwidth.amount,
556                   GNUNET_STRINGS_relative_time_to_string (res_delay,
557                                                           GNUNET_YES));
558       GNUNET_break (0);
559       GNUNET_SCHEDULER_shutdown ();
560       return;
561     }
562     break;
563
564   case GNUNET_NO:
565     GNUNET_break ((0 != amount) ||
566                   (0 != res_delay.rel_value_us));
567     break;
568
569   case GNUNET_SYSERR:
570     if ((amount != 0) ||
571         (0 == res_delay.rel_value_us))
572     {
573       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
574                   "Unexpectedly reserved %d bytes with delay %s!\n",
575                   (int) amount,
576                   GNUNET_STRINGS_relative_time_to_string (res_delay,
577                                                           GNUNET_YES));
578       GNUNET_break (0);
579       GNUNET_SCHEDULER_shutdown ();
580       return;
581     }
582     break;
583   }
584   off++;
585   run_interpreter ();
586 }
587
588
589 /**
590  * Main interpreter loop. Runs the steps of the test.
591  *
592  * @param cls NULL
593  */
594 static void
595 interpreter (void *cls)
596
597 {
598   struct Command *cmd;
599
600   interpreter_task = NULL;
601   while (1)
602   {
603     cmd = &test_commands[off];
604     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605                 "#%u: %d %s\n",
606                 off,
607                 (int) cmd->code,
608                 (NULL != cmd->label) ? cmd->label : "");
609     switch (cmd->code)
610     {
611     case CMD_END_PASS:
612       ret = 0;
613       GNUNET_SCHEDULER_shutdown ();
614       return;
615
616     case CMD_ADD_ADDRESS:
617       {
618         struct GNUNET_HELLO_Address *addr;
619         struct GNUNET_ATS_Session *session;
620
621         addr = make_address (cmd->details.add_address.pid,
622                              cmd->details.add_address.addr_num,
623                              cmd->details.add_address.addr_flags);
624         session = make_session (cmd->details.add_address.session);
625         if (cmd->details.add_address.expect_fail)
626           GNUNET_log_skip (1, GNUNET_NO);
627         cmd->details.add_address.ar
628           = GNUNET_ATS_address_add (sched_ats,
629                                     addr,
630                                     session,
631                                     &cmd->details.add_address.properties);
632         GNUNET_free (addr);
633         if (cmd->details.add_address.expect_fail)
634         {
635           GNUNET_log_skip (0, GNUNET_YES);
636         }
637         else if (NULL == cmd->details.add_address.ar)
638         {
639           GNUNET_break (0);
640           GNUNET_SCHEDULER_shutdown ();
641           return;
642         }
643         off++;
644         break;
645       }
646
647     case CMD_DEL_ADDRESS:
648       {
649         struct Command *add;
650
651         add = find_command (CMD_ADD_ADDRESS,
652                             cmd->details.del_address.add_label);
653         GNUNET_assert (NULL != add->details.add_address.ar);
654         GNUNET_ATS_address_destroy (add->details.add_address.ar);
655         add->details.add_address.ar = NULL;
656         off++;
657         break;
658       }
659
660     case CMD_AWAIT_ADDRESS_SUGGESTION:
661       {
662         struct GNUNET_PeerIdentity pid;
663         struct GNUNET_HELLO_Address *addr;
664         struct Command *add;
665         struct AddressSuggestData *asd;
666         int done;
667
668         make_peer (cmd->details.await_address_suggestion.pid,
669                    &pid);
670         asd = find_address_suggestion (&pid);
671         if (NULL == asd)
672           return;
673         if (GNUNET_NO == asd->active)
674           return;   /* last suggestion was to disconnect, wait longer */
675         done = GNUNET_YES;
676         if (NULL != cmd->details.await_address_suggestion.add_label)
677         {
678           done = GNUNET_NO;
679           add = find_command (CMD_ADD_ADDRESS,
680                               cmd->details.await_address_suggestion.add_label);
681           addr = make_address (add->details.add_address.pid,
682                                add->details.add_address.addr_num,
683                                add->details.add_address.addr_flags);
684           if ((asd->session ==
685                make_session (add->details.add_address.session)) &&
686               (0 ==
687                GNUNET_HELLO_address_cmp (addr,
688                                          asd->address)))
689             done = GNUNET_YES;
690           GNUNET_free (addr);
691         }
692         if (GNUNET_NO == done)
693           return;
694         off++;
695         break;
696       }
697
698     case CMD_AWAIT_DISCONNECT_SUGGESTION:
699       {
700         struct GNUNET_PeerIdentity pid;
701         struct AddressSuggestData *asd;
702
703         make_peer (cmd->details.await_disconnect_suggestion.pid,
704                    &pid);
705         asd = find_address_suggestion (&pid);
706         if (NULL == asd)
707           return;   /* odd, no suggestion at all yet!? */
708         if (GNUNET_YES == asd->active)
709           return;   /* last suggestion was to activate, wait longer */
710         /* last suggestion was to deactivate, condition satisfied! */
711         off++;
712         break;
713       }
714
715     case CMD_REQUEST_CONNECTION_START:
716       {
717         struct GNUNET_PeerIdentity pid;
718
719         make_peer (cmd->details.request_connection_start.pid,
720                    &pid);
721         cmd->details.request_connection_start.csh
722           = GNUNET_ATS_connectivity_suggest (con_ats,
723                                              &pid,
724                                              1);
725         off++;
726         break;
727       }
728
729     case CMD_REQUEST_CONNECTION_STOP:
730       {
731         struct Command *start;
732
733         start = find_command (CMD_REQUEST_CONNECTION_START,
734                               cmd->details.request_connection_stop.connect_label);
735         GNUNET_ATS_connectivity_suggest_cancel (
736           start->details.request_connection_start.csh);
737         start->details.request_connection_start.csh = NULL;
738         off++;
739         break;
740       }
741
742     case CMD_AWAIT_ADDRESS_INFORMATION:
743       {
744         struct AddressInformationData *aid;
745         struct Command *add;
746         struct Command *update;
747         struct GNUNET_HELLO_Address *addr;
748         const struct GNUNET_ATS_Properties *cmp;
749
750         add = find_command (CMD_ADD_ADDRESS,
751                             cmd->details.await_address_information.add_label);
752         update = find_command (CMD_UPDATE_ADDRESS,
753                                cmd->details.await_address_information.
754                                update_label);
755         addr = make_address (add->details.add_address.pid,
756                              add->details.add_address.addr_num,
757                              add->details.add_address.addr_flags);
758         aid = find_address_information (addr);
759         GNUNET_free (addr);
760         if (NULL == update)
761           cmp = &add->details.add_address.properties;
762         else
763           cmp = &update->details.update_address.properties;
764         if ((NULL != aid) &&
765             (cmp->delay.rel_value_us == aid->properties.delay.rel_value_us) &&
766             (cmp->utilization_out == aid->properties.utilization_out) &&
767             (cmp->utilization_in == aid->properties.utilization_in) &&
768             (cmp->distance == aid->properties.distance) &&
769             (cmp->scope == aid->properties.scope))
770         {
771           off++;
772           break;
773         }
774         return;
775       }
776
777     case CMD_UPDATE_ADDRESS:
778       {
779         struct Command *add;
780
781         add = find_command (CMD_ADD_ADDRESS,
782                             cmd->details.update_address.add_label);
783         GNUNET_assert (NULL != add->details.add_address.ar);
784         GNUNET_ATS_address_update (add->details.add_address.ar,
785                                    &cmd->details.update_address.properties);
786         off++;
787         break;
788       }
789
790     case CMD_ADD_SESSION:
791       {
792         struct Command *add;
793         struct GNUNET_ATS_Session *session;
794
795         add = find_command (CMD_ADD_ADDRESS,
796                             cmd->details.add_session.add_label);
797         session = make_session (cmd->details.add_session.session);
798         GNUNET_assert (NULL != add->details.add_address.ar);
799         GNUNET_ATS_address_add_session (add->details.add_address.ar,
800                                         session);
801         off++;
802         break;
803       }
804
805     case CMD_DEL_SESSION:
806       {
807         struct Command *add_address;
808         struct Command *add_session;
809         struct GNUNET_ATS_Session *session;
810
811         add_session = find_command (CMD_ADD_SESSION,
812                                     cmd->details.del_session.add_session_label);
813         add_address = find_command (CMD_ADD_ADDRESS,
814                                     add_session->details.add_session.add_label);
815         GNUNET_assert (NULL != add_address->details.add_address.ar);
816         session = make_session (add_session->details.add_session.session);
817         GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
818                                         session);
819         off++;
820         break;
821       }
822
823     case CMD_CHANGE_PREFERENCE:
824       {
825         struct GNUNET_PeerIdentity pid;
826
827         make_peer (cmd->details.change_preference.pid,
828                    &pid);
829         GNUNET_ATS_performance_change_preference (perf_ats,
830                                                   &pid,
831                                                   GNUNET_ATS_PREFERENCE_END);
832         off++;
833         break;
834       }
835
836     case CMD_PROVIDE_FEEDBACK:
837       {
838         struct GNUNET_PeerIdentity pid;
839
840         make_peer (cmd->details.provide_feedback.pid,
841                    &pid);
842         GNUNET_ATS_performance_give_feedback (perf_ats,
843                                               &pid,
844                                               cmd->details.provide_feedback.
845                                               scope,
846                                               GNUNET_ATS_PREFERENCE_END);
847         off++;
848         break;
849       }
850
851     case CMD_LIST_ADDRESSES:
852       {
853         struct GNUNET_PeerIdentity pid;
854
855         make_peer (cmd->details.list_addresses.pid,
856                    &pid);
857         cmd->details.list_addresses.alh
858           = GNUNET_ATS_performance_list_addresses (perf_ats,
859                                                    &pid,
860                                                    cmd->details.list_addresses.
861                                                    all,
862                                                    &info_cb,
863                                                    cmd);
864         return;
865       }
866
867     case CMD_RESERVE_BANDWIDTH:
868       {
869         struct GNUNET_PeerIdentity pid;
870
871         make_peer (cmd->details.reserve_bandwidth.pid,
872                    &pid);
873         cmd->details.reserve_bandwidth.rc
874           = GNUNET_ATS_reserve_bandwidth (perf_ats,
875                                           &pid,
876                                           cmd->details.reserve_bandwidth.amount,
877                                           &reservation_cb,
878                                           cmd);
879         return;
880       }
881
882     case CMD_SLEEP:
883       off++;
884       interpreter_task = GNUNET_SCHEDULER_add_delayed (cmd->details.sleep.delay,
885                                                        &interpreter,
886                                                        NULL);
887       return;
888     }     /* end switch */
889   }   /* end while(1) */
890 }
891
892
893 /**
894  * Signature of a function called by ATS with the current bandwidth
895  * and address preferences as determined by ATS.
896  *
897  * @param cls closure, should point to "asc-closure"
898  * @param peer for which we suggest an address, NULL if ATS connection died
899  * @param address suggested address (including peer identity of the peer),
900  *             may be NULL to signal disconnect from peer
901  * @param session session to use, NULL to establish a new outgoing session
902  * @param bandwidth_out assigned outbound bandwidth for the connection,
903  *        0 to signal disconnect
904  * @param bandwidth_in assigned inbound bandwidth for the connection,
905  *        0 to signal disconnect
906  */
907 static void
908 address_suggest_cb (void *cls,
909                     const struct GNUNET_PeerIdentity *peer,
910                     const struct GNUNET_HELLO_Address *address,
911                     struct GNUNET_ATS_Session *session,
912                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
913                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
914 {
915   const char *asc_cls = cls;
916   struct AddressSuggestData *asd;
917
918   GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
919   if (NULL == peer)
920   {
921     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
922                 "Connection to ATS died, likely a crash!\n");
923     GNUNET_SCHEDULER_shutdown ();
924 #if 0
925     /* This is what we should do if we wanted to continue past
926        the ATS crash. */
927     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
928                                            &free_asd,
929                                            NULL);
930     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
931                                            &free_aid,
932                                            NULL);
933 #endif
934     return;
935   }
936
937   asd = find_address_suggestion (peer);
938   if (NULL == asd)
939   {
940     asd = GNUNET_new (struct AddressSuggestData);
941     GNUNET_assert (GNUNET_YES ==
942                    GNUNET_CONTAINER_multipeermap_put (p2asd,
943                                                       peer,
944                                                       asd,
945                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
946   }
947   if ((0 == ntohl (bandwidth_out.value__)) &&
948       (0 == ntohl (bandwidth_in.value__)))
949     asd->active = GNUNET_NO;
950   else
951     asd->active = GNUNET_YES;
952   asd->bandwidth_out = bandwidth_out;
953   asd->bandwidth_in = bandwidth_in;
954   asd->session = session;
955   GNUNET_free_non_null (asd->address);
956   asd->address = NULL;
957   if (NULL != address)
958     asd->address = GNUNET_HELLO_address_copy (address);
959   if (NULL == interpreter_task)
960     run_interpreter ();
961 }
962
963
964 /**
965  * Signature of a function that is called with QoS information about an address.
966  *
967  * @param cls closure, should point to "aic-closure"
968  * @param address the address, NULL if ATS service was disconnected
969  * @param address_active #GNUNET_YES if this address is actively used
970  *        to maintain a connection to a peer;
971  *        #GNUNET_NO if the address is not actively used;
972  *        #GNUNET_SYSERR if this address is no longer available for ATS
973  * @param bandwidth_out assigned outbound bandwidth for the connection
974  * @param bandwidth_in assigned inbound bandwidth for the connection
975  * @param prop performance data for the address
976  */
977 static void
978 address_information_cb (void *cls,
979                         const struct GNUNET_HELLO_Address *address,
980                         int address_active,
981                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
982                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
983                         const struct GNUNET_ATS_Properties *prop)
984 {
985   const char *aic_cls = cls;
986   struct AddressInformationData *aid;
987
988   GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
989   if (NULL == address)
990   {
991     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
992                 "Connection to ATS died, likely a crash!\n");
993     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
994                                            &free_aid,
995                                            NULL);
996     return;
997   }
998
999   aid = find_address_information (address);
1000   if (NULL == aid)
1001   {
1002     aid = GNUNET_new (struct AddressInformationData);
1003     aid->address = GNUNET_HELLO_address_copy (address);
1004     GNUNET_assert (GNUNET_YES ==
1005                    GNUNET_CONTAINER_multipeermap_put (p2aid,
1006                                                       &address->peer,
1007                                                       aid,
1008                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
1009   }
1010   aid->active = address_active;
1011   aid->bandwidth_out = bandwidth_out;
1012   aid->bandwidth_in = bandwidth_in;
1013   aid->properties = *prop;
1014   if (NULL == interpreter_task)
1015     run_interpreter ();
1016 }
1017
1018
1019 /**
1020  * Function run once the ATS service has been started.
1021  *
1022  * @param cls NULL
1023  * @param cfg configuration for the testcase
1024  * @param peer handle to the peer
1025  */
1026 static void
1027 run (void *cls,
1028      const struct GNUNET_CONFIGURATION_Handle *cfg,
1029      struct GNUNET_TESTING_Peer *peer)
1030 {
1031   p2asd = GNUNET_CONTAINER_multipeermap_create (128,
1032                                                 GNUNET_NO);
1033   p2aid = GNUNET_CONTAINER_multipeermap_create (128,
1034                                                 GNUNET_NO);
1035   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
1036                                 &end,
1037                                 NULL);
1038
1039   sched_ats = GNUNET_ATS_scheduling_init (cfg,
1040                                           &address_suggest_cb,
1041                                           "asc-closure");
1042   if (NULL == sched_ats)
1043   {
1044     GNUNET_break (0);
1045     GNUNET_SCHEDULER_shutdown ();
1046     return;
1047   }
1048   con_ats = GNUNET_ATS_connectivity_init (cfg);
1049   if (NULL == con_ats)
1050   {
1051     GNUNET_break (0);
1052     GNUNET_SCHEDULER_shutdown ();
1053     return;
1054   }
1055   perf_ats = GNUNET_ATS_performance_init (cfg,
1056                                           &address_information_cb,
1057                                           "aic-closure");
1058   if (NULL == perf_ats)
1059   {
1060     GNUNET_break (0);
1061     GNUNET_SCHEDULER_shutdown ();
1062     return;
1063   }
1064   run_interpreter ();
1065 }
1066
1067
1068 /**
1069  * Run ATS test.
1070  *
1071  * @param argc length of @a argv
1072  * @param argv command line
1073  * @param cmds commands to run with the interpreter
1074  * @param timeout how long is the test allowed to take?
1075  * @return 0 on success
1076  */
1077 int
1078 TEST_ATS_run (int argc,
1079               char *argv[],
1080               struct Command *cmds,
1081               struct GNUNET_TIME_Relative timeout)
1082 {
1083   char *test_filename = GNUNET_strdup (argv[0]);
1084   char *sep;
1085   char *config_file;
1086   char *underscore;
1087
1088   test_commands = cmds;
1089   TIMEOUT = timeout;
1090   if (NULL != (sep = strstr (test_filename, ".exe")))
1091     sep[0] = '\0';
1092   underscore = strrchr (test_filename, (int) '_');
1093   GNUNET_assert (NULL != underscore);
1094   GNUNET_asprintf (&config_file,
1095                    "test_ats_api_%s.conf",
1096                    underscore + 1);
1097   ret = 2;
1098   if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
1099                                     config_file,
1100                                     &run, NULL))
1101     ret = 1;
1102   GNUNET_free (test_filename);
1103   GNUNET_free (config_file);
1104   return ret;
1105 }
1106
1107 /* end of test_ats_lib.c */