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