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