- refactor kx sending, unify under send_kx
[oweals/gnunet.git] / src / ats / test_ats_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 Christian Grothoff (and other contributing authors)
4
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.
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      General Public License for more details.
14
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.
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 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  * @param tc unused
283  */
284 static void
285 end (void *cls,
286      const struct GNUNET_SCHEDULER_TaskContext *tc)
287 {
288   if (0 != ret)
289     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
290                 "Test failed at stage %u %s\n",
291                 off,
292                 (NULL != test_commands[off].label)
293                 ? test_commands[off].label
294                 : "");
295   if (NULL != interpreter_task)
296   {
297     GNUNET_SCHEDULER_cancel (interpreter_task);
298     interpreter_task = NULL;
299   }
300   if (NULL != sched_ats)
301   {
302     GNUNET_ATS_scheduling_done (sched_ats);
303     sched_ats = NULL;
304   }
305   if (NULL != con_ats)
306   {
307     GNUNET_ATS_connectivity_done (con_ats);
308     con_ats = NULL;
309   }
310   if (NULL != perf_ats)
311   {
312     GNUNET_ATS_performance_done (perf_ats);
313     perf_ats = NULL;
314   }
315   if (NULL != p2asd)
316   {
317     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
318                                            &free_asd,
319                                            NULL);
320     GNUNET_CONTAINER_multipeermap_destroy (p2asd);
321     p2asd = NULL;
322   }
323   if (NULL != p2aid)
324   {
325     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
326                                            &free_aid,
327                                            NULL);
328     GNUNET_CONTAINER_multipeermap_destroy (p2aid);
329     p2aid = NULL;
330   }
331 }
332
333
334 /**
335  * Main interpreter loop. Runs the steps of the test.
336  *
337  * @param cls NULL
338  * @param tc unused
339  */
340 static void
341 interpreter (void *cls,
342              const struct GNUNET_SCHEDULER_TaskContext *tc);
343
344
345 /**
346  * Run the interpreter next.
347  */
348 static void
349 run_interpreter ()
350 {
351   if (NULL != interpreter_task)
352     GNUNET_SCHEDULER_cancel (interpreter_task);
353   interpreter_task = GNUNET_SCHEDULER_add_now (&interpreter,
354                                                NULL);
355 }
356
357
358 /**
359  * Initialize public key of a peer based on a single number.
360  *
361  * @param pid number to use as the basis
362  * @param pk resulting fake public key
363  */
364 static void
365 make_peer (uint32_t pid,
366            struct GNUNET_PeerIdentity *pk)
367 {
368   memset (pk,
369           (int) pid,
370           sizeof (struct GNUNET_PeerIdentity));
371   memcpy (pk,
372           &pid,
373           sizeof (uint32_t));
374 }
375
376
377 /**
378  * Generate a fake address based on the given parameters.
379  *
380  * @param pid number of the peer
381  * @param num number of the address at peer @a pid
382  * @param addr_flags flags to use for the address
383  * @return the address
384  */
385 static struct GNUNET_HELLO_Address *
386 make_address (uint32_t pid,
387               uint32_t num,
388               enum GNUNET_HELLO_AddressInfo addr_flags)
389 {
390   struct GNUNET_PeerIdentity pk;
391   uint32_t nbo;
392
393   nbo = htonl (num);
394   make_peer (pid,
395              &pk);
396   return GNUNET_HELLO_address_allocate (&pk,
397                                         "test",
398                                         &nbo,
399                                         sizeof (nbo),
400                                         addr_flags);
401 }
402
403
404 /**
405  * Our dummy sessions.
406  */
407 struct Session
408 {
409   /**
410    * Field to avoid `0 == sizeof(struct Session)`.
411    */
412   unsigned int non_empty;
413 };
414
415
416 /**
417  * Create a session instance for ATS.
418  *
419  * @param i which session number to return
420  * @return NULL if @a i is 0, otherwise a pointer unique to @a i
421  */
422 static struct Session *
423 make_session (unsigned int i)
424 {
425   struct Session *baseptr = NULL;
426
427   if (0 == i)
428     return NULL;
429   /* Yes, these are *intentionally* out-of-bounds,
430      and offset from NULL, as nobody should ever
431      use those other than to compare pointers! */
432   return baseptr + i;
433 }
434
435
436 /**
437  * Find a @a code command before the global #off with the
438  * specified @a label.
439  *
440  * @param code opcode to look for
441  * @param label label to look for, NULL for none
442  * @return previous command with the matching label
443  */
444 static struct Command *
445 find_command (enum CommandCode code,
446               const char *label)
447 {
448   int i;
449
450   if (NULL == label)
451     return NULL;
452   for (i=off-1;i>=0;i--)
453     if ( (code == test_commands[i].code) &&
454          (0 == strcmp (test_commands[i].label,
455                        label)) )
456       return &test_commands[i];
457   GNUNET_break (0);
458   return NULL;
459 }
460
461
462 /**
463  * Function called from #GNUNET_ATS_performance_list_addresses when
464  * we process a #CMD_LIST_ADDRESSES command.
465  *
466  * @param cls the `struct Command` that caused the call
467  * @param address the address, NULL if ATS service was disconnected
468  * @param address_active #GNUNET_YES if this address is actively used
469  *        to maintain a connection to a peer;
470  *        #GNUNET_NO if the address is not actively used;
471  *        #GNUNET_SYSERR if this address is no longer available for ATS
472  * @param bandwidth_out assigned outbound bandwidth for the connection
473  * @param bandwidth_in assigned inbound bandwidth for the connection
474  * @param prop performance data for the address
475  */
476 static void
477 info_cb (void *cls,
478          const struct GNUNET_HELLO_Address *address,
479          int address_active,
480          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
481          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
482          const struct GNUNET_ATS_Properties *prop)
483 {
484   struct Command *c = cls;
485   struct CommandListAddresses *cmd = &c->details.list_addresses;
486
487   if (NULL == address)
488   {
489     cmd->alh = NULL;
490     /* we are done with the iteration, continue to execute */
491     if ( (cmd->calls < cmd->min_calls) &&
492          (cmd->active_calls < cmd->min_active_calls) )
493     {
494       GNUNET_SCHEDULER_shutdown ();
495       return;
496     }
497     off++;
498     run_interpreter ();
499     return;
500   }
501   switch (address_active)
502   {
503   case GNUNET_YES:
504     cmd->active_calls++;
505     cmd->calls++;
506     break;
507   case GNUNET_NO:
508     cmd->calls++;
509     break;
510   case GNUNET_SYSERR:
511     return;
512   }
513   if ( (cmd->calls > cmd->max_calls) &&
514        (cmd->active_calls < cmd->max_active_calls) )
515   {
516     GNUNET_break (0);
517     GNUNET_ATS_performance_list_addresses_cancel (cmd->alh);
518     cmd->alh = NULL;
519     GNUNET_SCHEDULER_shutdown ();
520     return;
521   }
522 }
523
524
525 /**
526  * Function called with reservation result.
527  *
528  * @param cls closure with the reservation command (`struct Command`)
529  * @param peer identifies the peer
530  * @param amount set to the amount that was actually reserved or unreserved;
531  *               either the full requested amount or zero (no partial reservations)
532  * @param res_delay if the reservation could not be satisfied (amount was 0), how
533  *        long should the client wait until re-trying?
534  */
535 static void
536 reservation_cb (void *cls,
537                 const struct GNUNET_PeerIdentity *peer,
538                 int32_t amount,
539                 struct GNUNET_TIME_Relative res_delay)
540 {
541   struct Command *cmd = cls;
542   struct GNUNET_PeerIdentity pid;
543
544   cmd->details.reserve_bandwidth.rc = NULL;
545   make_peer (cmd->details.reserve_bandwidth.pid,
546              &pid);
547   GNUNET_assert (0 == memcmp (peer,
548                               &pid,
549                               sizeof (struct GNUNET_PeerIdentity)));
550   switch (cmd->details.reserve_bandwidth.expected_result)
551   {
552   case GNUNET_OK:
553     if (amount != cmd->details.reserve_bandwidth.amount)
554     {
555       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
556                   "Unexpectedly failed to reserve %d/%d bytes with delay %s!\n",
557                   (int) amount,
558                   (int) cmd->details.reserve_bandwidth.amount,
559                   GNUNET_STRINGS_relative_time_to_string (res_delay,
560                                                           GNUNET_YES));
561       GNUNET_break (0);
562       GNUNET_SCHEDULER_shutdown ();
563       return;
564     }
565     break;
566   case GNUNET_NO:
567     GNUNET_break ( (0 != amount) ||
568                    (0 != res_delay.rel_value_us) );
569     break;
570   case GNUNET_SYSERR:
571     if ( (amount != 0) ||
572          (0 == res_delay.rel_value_us) )
573     {
574       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
575                   "Unexpectedly reserved %d bytes with delay %s!\n",
576                   (int) amount,
577                   GNUNET_STRINGS_relative_time_to_string (res_delay,
578                                                           GNUNET_YES));
579       GNUNET_break (0);
580       GNUNET_SCHEDULER_shutdown ();
581       return;
582     }
583     break;
584   }
585   off++;
586   run_interpreter ();
587 }
588
589
590 /**
591  * Main interpreter loop. Runs the steps of the test.
592  *
593  * @param cls NULL
594  * @param tc unused
595  */
596 static void
597 interpreter (void *cls,
598              const struct GNUNET_SCHEDULER_TaskContext *tc)
599
600 {
601   struct Command *cmd;
602
603   interpreter_task = NULL;
604   while (1)
605   {
606     cmd = &test_commands[off];
607     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608                 "#%u: %d %s\n",
609                 off,
610                 (int) cmd->code,
611                 (NULL != cmd->label) ? cmd->label : "");
612     switch (cmd->code)
613     {
614     case CMD_END_PASS:
615       ret = 0;
616       GNUNET_SCHEDULER_shutdown ();
617       return;
618     case CMD_ADD_ADDRESS:
619       {
620         struct GNUNET_HELLO_Address *addr;
621         struct Session *session;
622
623         addr = make_address (cmd->details.add_address.pid,
624                              cmd->details.add_address.addr_num,
625                              cmd->details.add_address.addr_flags);
626         session = make_session (cmd->details.add_address.session);
627         if (cmd->details.add_address.expect_fail)
628           GNUNET_log_skip (1, GNUNET_NO);
629         cmd->details.add_address.ar
630           = GNUNET_ATS_address_add (sched_ats,
631                                     addr,
632                                     session,
633                                     &cmd->details.add_address.properties);
634         GNUNET_free (addr);
635         if (cmd->details.add_address.expect_fail)
636         {
637           GNUNET_log_skip (0, GNUNET_YES);
638         }
639         else if (NULL == cmd->details.add_address.ar)
640         {
641           GNUNET_break (0);
642           GNUNET_SCHEDULER_shutdown ();
643           return;
644         }
645         off++;
646         break;
647       }
648     case CMD_DEL_ADDRESS:
649       {
650         struct Command *add;
651
652         add = find_command (CMD_ADD_ADDRESS,
653                             cmd->details.del_address.add_label);
654         GNUNET_assert (NULL != add->details.add_address.ar);
655         GNUNET_ATS_address_destroy (add->details.add_address.ar);
656         add->details.add_address.ar = NULL;
657         off++;
658         break;
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;
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     case CMD_AWAIT_DISCONNECT_SUGGESTION:
698       {
699         struct GNUNET_PeerIdentity pid;
700         struct AddressSuggestData *asd;
701
702         make_peer (cmd->details.await_disconnect_suggestion.pid,
703                    &pid);
704         asd = find_address_suggestion (&pid);
705         if (NULL == asd)
706           return;
707         if (GNUNET_NO == asd->active)
708           return;
709         off++;
710         break;
711       }
712     case CMD_REQUEST_CONNECTION_START:
713       {
714         struct GNUNET_PeerIdentity pid;
715
716         make_peer (cmd->details.request_connection_start.pid,
717                    &pid);
718         cmd->details.request_connection_start.csh
719           = GNUNET_ATS_connectivity_suggest (con_ats,
720                                              &pid);
721         off++;
722         break;
723       }
724     case CMD_REQUEST_CONNECTION_STOP:
725       {
726         struct Command *start;
727
728         start = find_command (CMD_REQUEST_CONNECTION_START,
729                               cmd->details.request_connection_stop.connect_label);
730         GNUNET_ATS_connectivity_suggest_cancel (start->details.request_connection_start.csh);
731         start->details.request_connection_start.csh = NULL;
732         off++;
733         break;
734       }
735     case CMD_AWAIT_ADDRESS_INFORMATION:
736       {
737         struct AddressInformationData *aid;
738         struct Command *add;
739         struct Command *update;
740         struct GNUNET_HELLO_Address *addr;
741         const struct GNUNET_ATS_Properties *cmp;
742
743         add = find_command (CMD_ADD_ADDRESS,
744                             cmd->details.await_address_information.add_label);
745         update = find_command (CMD_UPDATE_ADDRESS,
746                                cmd->details.await_address_information.update_label);
747         addr = make_address (add->details.add_address.pid,
748                              add->details.add_address.addr_num,
749                              add->details.add_address.addr_flags);
750         aid = find_address_information (addr);
751         GNUNET_free (addr);
752         if (NULL == update)
753           cmp = &add->details.add_address.properties;
754         else
755           cmp = &update->details.update_address.properties;
756         if ( (NULL != aid) &&
757              (0 == memcmp (cmp,
758                            &aid->properties,
759                            sizeof (struct GNUNET_ATS_Properties))) )
760         {
761           off++;
762           break;
763         }
764         return;
765       }
766     case CMD_UPDATE_ADDRESS:
767       {
768         struct Command *add;
769
770         add = find_command (CMD_ADD_ADDRESS,
771                             cmd->details.update_address.add_label);
772         GNUNET_assert (NULL != add->details.add_address.ar);
773         GNUNET_ATS_address_update (add->details.add_address.ar,
774                                    &cmd->details.update_address.properties);
775         off++;
776         break;
777       }
778     case CMD_ADD_SESSION:
779       {
780         struct Command *add;
781         struct Session *session;
782
783         add = find_command (CMD_ADD_ADDRESS,
784                             cmd->details.add_session.add_label);
785         session = make_session (cmd->details.add_session.session);
786         GNUNET_assert (NULL != add->details.add_address.ar);
787         GNUNET_ATS_address_add_session (add->details.add_address.ar,
788                                         session);
789         off++;
790         break;
791       }
792     case CMD_DEL_SESSION:
793       {
794         struct Command *add_address;
795         struct Command *add_session;
796         struct Session *session;
797
798         add_session = find_command (CMD_ADD_SESSION,
799                                     cmd->details.del_session.add_session_label);
800         add_address = find_command (CMD_ADD_ADDRESS,
801                                     add_session->details.add_session.add_label);
802         GNUNET_assert (NULL != add_address->details.add_address.ar);
803         session = make_session (add_session->details.add_session.session);
804         GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
805                                         session);
806         off++;
807         break;
808       }
809     case CMD_CHANGE_PREFERENCE:
810       {
811         struct GNUNET_PeerIdentity pid;
812
813         make_peer (cmd->details.change_preference.pid,
814                    &pid);
815         GNUNET_ATS_performance_change_preference (perf_ats,
816                                                   &pid,
817                                                   GNUNET_ATS_PREFERENCE_END);
818         off++;
819         break;
820       }
821     case CMD_PROVIDE_FEEDBACK:
822       {
823         struct GNUNET_PeerIdentity pid;
824
825         make_peer (cmd->details.provide_feedback.pid,
826                    &pid);
827         GNUNET_ATS_performance_give_feedback (perf_ats,
828                                               &pid,
829                                               cmd->details.provide_feedback.scope,
830                                               GNUNET_ATS_PREFERENCE_END);
831         off++;
832         break;
833       }
834     case CMD_LIST_ADDRESSES:
835       {
836         struct GNUNET_PeerIdentity pid;
837
838         make_peer (cmd->details.list_addresses.pid,
839                    &pid);
840         cmd->details.list_addresses.alh
841           = GNUNET_ATS_performance_list_addresses (perf_ats,
842                                                    &pid,
843                                                    cmd->details.list_addresses.all,
844                                                    &info_cb,
845                                                    cmd);
846         return;
847       }
848     case CMD_RESERVE_BANDWIDTH:
849       {
850         struct GNUNET_PeerIdentity pid;
851
852         make_peer (cmd->details.reserve_bandwidth.pid,
853                    &pid);
854         cmd->details.reserve_bandwidth.rc
855           = GNUNET_ATS_reserve_bandwidth (perf_ats,
856                                           &pid,
857                                           cmd->details.reserve_bandwidth.amount,
858                                           &reservation_cb,
859                                           cmd);
860         return;
861       }
862     case CMD_SLEEP:
863       off++;
864       interpreter_task = GNUNET_SCHEDULER_add_delayed (cmd->details.sleep.delay,
865                                                        &interpreter,
866                                                        NULL);
867       return;
868     } /* end switch */
869   } /* end while(1) */
870 }
871
872
873 /**
874  * Signature of a function called by ATS with the current bandwidth
875  * and address preferences as determined by ATS.
876  *
877  * @param cls closure, should point to "asc-closure"
878  * @param peer for which we suggest an address, NULL if ATS connection died
879  * @param address suggested address (including peer identity of the peer),
880  *             may be NULL to signal disconnect from peer
881  * @param session session to use, NULL to establish a new outgoing session
882  * @param bandwidth_out assigned outbound bandwidth for the connection,
883  *        0 to signal disconnect
884  * @param bandwidth_in assigned inbound bandwidth for the connection,
885  *        0 to signal disconnect
886  */
887 static void
888 address_suggest_cb (void *cls,
889                     const struct GNUNET_PeerIdentity *peer,
890                     const struct GNUNET_HELLO_Address *address,
891                     struct Session *session,
892                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
893                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
894 {
895   const char *asc_cls = cls;
896   struct AddressSuggestData *asd;
897
898   GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
899   if (NULL == peer)
900   {
901     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
902                 "Connection to ATS died, likely a crash!\n");
903     GNUNET_SCHEDULER_shutdown ();
904 #if 0
905     /* This is what we should do if we wanted to continue past
906        the ATS crash. */
907     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
908                                            &free_asd,
909                                            NULL);
910     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
911                                            &free_aid,
912                                            NULL);
913 #endif
914     return;
915   }
916
917   asd = find_address_suggestion (peer);
918   if (NULL == asd)
919   {
920     asd = GNUNET_new (struct AddressSuggestData);
921     GNUNET_assert (GNUNET_YES ==
922                    GNUNET_CONTAINER_multipeermap_put (p2asd,
923                                                       peer,
924                                                       asd,
925                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
926   }
927   if ( (0 == ntohl (bandwidth_out.value__)) &&
928        (0 == ntohl (bandwidth_in.value__)) )
929     asd->active = GNUNET_NO;
930   else
931     asd->active = GNUNET_YES;
932   asd->bandwidth_out = bandwidth_out;
933   asd->bandwidth_in = bandwidth_in;
934   asd->session = session;
935   GNUNET_free_non_null (asd->address);
936   asd->address = NULL;
937   if (NULL != address)
938     asd->address = GNUNET_HELLO_address_copy (address);
939   if (NULL == interpreter_task)
940     run_interpreter ();
941 }
942
943
944 /**
945  * Signature of a function that is called with QoS information about an address.
946  *
947  * @param cls closure, should point to "aic-closure"
948  * @param address the address, NULL if ATS service was disconnected
949  * @param address_active #GNUNET_YES if this address is actively used
950  *        to maintain a connection to a peer;
951  *        #GNUNET_NO if the address is not actively used;
952  *        #GNUNET_SYSERR if this address is no longer available for ATS
953  * @param bandwidth_out assigned outbound bandwidth for the connection
954  * @param bandwidth_in assigned inbound bandwidth for the connection
955  * @param prop performance data for the address
956  */
957 static void
958 address_information_cb (void *cls,
959                         const struct GNUNET_HELLO_Address *address,
960                         int address_active,
961                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
962                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
963                         const struct GNUNET_ATS_Properties *prop)
964 {
965   const char *aic_cls = cls;
966   struct AddressInformationData *aid;
967
968   GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
969   if (NULL == address)
970   {
971     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
972                 "Connection to ATS died, likely a crash!\n");
973     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
974                                            &free_aid,
975                                            NULL);
976     return;
977   }
978
979   aid = find_address_information (address);
980   if (NULL == aid)
981   {
982     aid = GNUNET_new (struct AddressInformationData);
983     aid->address = GNUNET_HELLO_address_copy (address);
984     GNUNET_assert (GNUNET_YES ==
985                    GNUNET_CONTAINER_multipeermap_put (p2aid,
986                                                       &address->peer,
987                                                       aid,
988                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
989   }
990   aid->active = address_active;
991   aid->bandwidth_out = bandwidth_out;
992   aid->bandwidth_in = bandwidth_in;
993   aid->properties = *prop;
994   if (NULL == interpreter_task)
995     run_interpreter ();
996 }
997
998
999 /**
1000  * Function run once the ATS service has been started.
1001  *
1002  * @param cls NULL
1003  * @param cfg configuration for the testcase
1004  * @param peer handle to the peer
1005  */
1006 static void
1007 run (void *cls,
1008      const struct GNUNET_CONFIGURATION_Handle *cfg,
1009      struct GNUNET_TESTING_Peer *peer)
1010 {
1011   p2asd = GNUNET_CONTAINER_multipeermap_create (128,
1012                                                 GNUNET_NO);
1013   p2aid = GNUNET_CONTAINER_multipeermap_create (128,
1014                                                 GNUNET_NO);
1015   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
1016                                 &end,
1017                                 NULL);
1018
1019   sched_ats = GNUNET_ATS_scheduling_init (cfg,
1020                                           &address_suggest_cb,
1021                                           "asc-closure");
1022   if (NULL == sched_ats)
1023   {
1024     GNUNET_break (0);
1025     GNUNET_SCHEDULER_shutdown ();
1026     return;
1027   }
1028   con_ats = GNUNET_ATS_connectivity_init (cfg);
1029   if (NULL == con_ats)
1030   {
1031     GNUNET_break (0);
1032     GNUNET_SCHEDULER_shutdown ();
1033     return;
1034   }
1035   perf_ats = GNUNET_ATS_performance_init (cfg,
1036                                           &address_information_cb,
1037                                           "aic-closure");
1038   if (NULL == perf_ats)
1039   {
1040     GNUNET_break (0);
1041     GNUNET_SCHEDULER_shutdown ();
1042     return;
1043   }
1044   run_interpreter ();
1045 }
1046
1047
1048 /**
1049  * Run ATS test.
1050  *
1051  * @param argc length of @a argv
1052  * @param argv command line
1053  * @param cmds commands to run with the interpreter
1054  * @param timeout how long is the test allowed to take?
1055  * @return 0 on success
1056  */
1057 int
1058 TEST_ATS_run (int argc,
1059               char *argv[],
1060               struct Command *cmds,
1061               struct GNUNET_TIME_Relative timeout)
1062 {
1063   char *test_filename = GNUNET_strdup (argv[0]);
1064   char *sep;
1065   char *config_file;
1066   char *underscore;
1067
1068   test_commands = cmds;
1069   TIMEOUT = timeout;
1070   if (NULL != (sep = strstr (test_filename, ".exe")))
1071     sep[0] = '\0';
1072   underscore = strrchr (test_filename, (int) '_');
1073   GNUNET_assert (NULL != underscore);
1074   GNUNET_asprintf (&config_file,
1075                    "test_ats_api_%s.conf",
1076                    underscore + 1);
1077   ret = 2;
1078   if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
1079                                     config_file,
1080                                     &run, NULL))
1081     ret = 1;
1082   GNUNET_free (test_filename);
1083   GNUNET_free (config_file);
1084   return ret;
1085 }
1086
1087 /* end of test_ats_lib.c */