add command to test reservations
[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 (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   make_peer (cmd->details.reserve_bandwidth.pid,
545              &pid);
546   GNUNET_assert (0 == memcmp (peer,
547                               &pid,
548                               sizeof (struct GNUNET_PeerIdentity)));
549   switch (cmd->details.reserve_bandwidth.expected_result)
550   {
551   case GNUNET_OK:
552     if (amount != cmd->details.reserve_bandwidth.amount)
553     {
554       GNUNET_break (0);
555       GNUNET_SCHEDULER_shutdown ();
556       return;
557     }
558     break;
559   case GNUNET_NO:
560     GNUNET_break ( (0 != amount) ||
561                    (0 != res_delay.rel_value_us) );
562     break;
563   case GNUNET_SYSERR:
564     if ( (amount != cmd->details.reserve_bandwidth.amount) ||
565          (0 == res_delay.rel_value_us) )
566     {
567       GNUNET_break (0);
568       GNUNET_SCHEDULER_shutdown ();
569       return;
570     }
571     break;
572   }
573   off++;
574   run_interpreter ();
575 }
576
577
578 /**
579  * Main interpreter loop. Runs the steps of the test.
580  *
581  * @param cls NULL
582  * @param tc unused
583  */
584 static void
585 interpreter (void *cls,
586              const struct GNUNET_SCHEDULER_TaskContext *tc)
587
588 {
589   struct Command *cmd;
590
591   interpreter_task = NULL;
592   while (1)
593   {
594     cmd = &test_commands[off];
595     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
596                 "#%u: %d %s\n",
597                 off,
598                 (int) cmd->code,
599                 (NULL != cmd->label) ? cmd->label : "");
600     switch (cmd->code)
601     {
602     case CMD_END_PASS:
603       ret = 0;
604       GNUNET_SCHEDULER_shutdown ();
605       return;
606     case CMD_ADD_ADDRESS:
607       {
608         struct GNUNET_HELLO_Address *addr;
609         struct Session *session;
610
611         addr = make_address (cmd->details.add_address.pid,
612                              cmd->details.add_address.addr_num,
613                              cmd->details.add_address.addr_flags);
614         session = make_session (cmd->details.add_address.session);
615         if (cmd->details.add_address.expect_fail)
616           GNUNET_log_skip (1, GNUNET_NO);
617         cmd->details.add_address.ar
618           = GNUNET_ATS_address_add (sched_ats,
619                                     addr,
620                                     session,
621                                     &cmd->details.add_address.properties);
622         GNUNET_free (addr);
623         if (cmd->details.add_address.expect_fail)
624         {
625           GNUNET_log_skip (0, GNUNET_YES);
626         }
627         else if (NULL == cmd->details.add_address.ar)
628         {
629           GNUNET_break (0);
630           GNUNET_SCHEDULER_shutdown ();
631           return;
632         }
633         off++;
634         break;
635       }
636     case CMD_DEL_ADDRESS:
637       {
638         struct Command *add;
639
640         add = find_command (CMD_ADD_ADDRESS,
641                             cmd->details.del_address.add_label);
642         GNUNET_assert (NULL != add->details.add_address.ar);
643         GNUNET_ATS_address_destroy (add->details.add_address.ar);
644         add->details.add_address.ar = NULL;
645         off++;
646         break;
647       }
648     case CMD_AWAIT_ADDRESS_SUGGESTION:
649       {
650         struct GNUNET_PeerIdentity pid;
651         struct GNUNET_HELLO_Address *addr;
652         struct Command *add;
653         struct AddressSuggestData *asd;
654         int done;
655
656         make_peer (cmd->details.await_address_suggestion.pid,
657                    &pid);
658         asd = find_address_suggestion (&pid);
659         if (NULL == asd)
660           return;
661         if (GNUNET_NO == asd->active)
662           return;
663         done = GNUNET_YES;
664         if (NULL != cmd->details.await_address_suggestion.add_label)
665         {
666           done = GNUNET_NO;
667           add = find_command (CMD_ADD_ADDRESS,
668                               cmd->details.await_address_suggestion.add_label);
669           addr = make_address (add->details.add_address.pid,
670                                add->details.add_address.addr_num,
671                                add->details.add_address.addr_flags);
672           if ( (asd->session ==
673                 make_session (add->details.add_address.session)) &&
674                (0 ==
675                 GNUNET_HELLO_address_cmp (addr,
676                                           asd->address)) )
677             done = GNUNET_YES;
678           GNUNET_free (addr);
679         }
680         if (GNUNET_NO == done)
681           return;
682         off++;
683         break;
684       }
685     case CMD_AWAIT_DISCONNECT_SUGGESTION:
686       {
687         struct GNUNET_PeerIdentity pid;
688         struct AddressSuggestData *asd;
689
690         make_peer (cmd->details.await_disconnect_suggestion.pid,
691                    &pid);
692         asd = find_address_suggestion (&pid);
693         if (NULL == asd)
694           return;
695         if (GNUNET_NO == asd->active)
696           return;
697         off++;
698         break;
699       }
700     case CMD_REQUEST_CONNECTION_START:
701       {
702         struct GNUNET_PeerIdentity pid;
703
704         make_peer (cmd->details.request_connection_start.pid,
705                    &pid);
706         cmd->details.request_connection_start.csh
707           = GNUNET_ATS_connectivity_suggest (con_ats,
708                                              &pid);
709         off++;
710         break;
711       }
712     case CMD_REQUEST_CONNECTION_STOP:
713       {
714         struct Command *start;
715
716         start = find_command (CMD_REQUEST_CONNECTION_START,
717                               cmd->details.request_connection_stop.connect_label);
718         GNUNET_ATS_connectivity_suggest_cancel (start->details.request_connection_start.csh);
719         start->details.request_connection_start.csh = NULL;
720         off++;
721         break;
722       }
723     case CMD_AWAIT_ADDRESS_INFORMATION:
724       {
725         struct AddressInformationData *aid;
726         struct Command *add;
727         struct Command *update;
728         struct GNUNET_HELLO_Address *addr;
729         const struct GNUNET_ATS_Properties *cmp;
730
731         add = find_command (CMD_ADD_ADDRESS,
732                             cmd->details.await_address_information.add_label);
733         update = find_command (CMD_UPDATE_ADDRESS,
734                                cmd->details.await_address_information.update_label);
735         addr = make_address (add->details.add_address.pid,
736                              add->details.add_address.addr_num,
737                              add->details.add_address.addr_flags);
738         aid = find_address_information (addr);
739         GNUNET_free (addr);
740         if (NULL == update)
741           cmp = &add->details.add_address.properties;
742         else
743           cmp = &update->details.update_address.properties;
744         if ( (NULL != aid) &&
745              (0 == memcmp (cmp,
746                            &aid->properties,
747                            sizeof (struct GNUNET_ATS_Properties))) )
748         {
749           off++;
750           break;
751         }
752         return;
753       }
754     case CMD_UPDATE_ADDRESS:
755       {
756         struct Command *add;
757
758         add = find_command (CMD_ADD_ADDRESS,
759                             cmd->details.update_address.add_label);
760         GNUNET_assert (NULL != add->details.add_address.ar);
761         GNUNET_ATS_address_update (add->details.add_address.ar,
762                                    &cmd->details.update_address.properties);
763         off++;
764         break;
765       }
766     case CMD_ADD_SESSION:
767       {
768         struct Command *add;
769         struct Session *session;
770
771         add = find_command (CMD_ADD_ADDRESS,
772                             cmd->details.add_session.add_label);
773         session = make_session (cmd->details.add_session.session);
774         GNUNET_assert (NULL != add->details.add_address.ar);
775         GNUNET_ATS_address_add_session (add->details.add_address.ar,
776                                         session);
777         off++;
778         break;
779       }
780     case CMD_DEL_SESSION:
781       {
782         struct Command *add_address;
783         struct Command *add_session;
784         struct Session *session;
785
786         add_session = find_command (CMD_ADD_SESSION,
787                                     cmd->details.del_session.add_session_label);
788         add_address = find_command (CMD_ADD_ADDRESS,
789                                     add_session->details.add_session.add_label);
790         GNUNET_assert (NULL != add_address->details.add_address.ar);
791         session = make_session (add_session->details.add_session.session);
792         GNUNET_ATS_address_del_session (add_address->details.add_address.ar,
793                                         session);
794         off++;
795         break;
796       }
797     case CMD_CHANGE_PREFERENCE:
798       {
799         struct GNUNET_PeerIdentity pid;
800
801         make_peer (cmd->details.change_preference.pid,
802                    &pid);
803         GNUNET_ATS_performance_change_preference (perf_ats,
804                                                   &pid,
805                                                   GNUNET_ATS_PREFERENCE_END);
806         off++;
807         break;
808       }
809     case CMD_PROVIDE_FEEDBACK:
810       {
811         struct GNUNET_PeerIdentity pid;
812
813         make_peer (cmd->details.provide_feedback.pid,
814                    &pid);
815         GNUNET_ATS_performance_give_feedback (perf_ats,
816                                               &pid,
817                                               cmd->details.provide_feedback.scope,
818                                               GNUNET_ATS_PREFERENCE_END);
819         off++;
820         break;
821       }
822     case CMD_LIST_ADDRESSES:
823       {
824         struct GNUNET_PeerIdentity pid;
825
826         make_peer (cmd->details.list_addresses.pid,
827                    &pid);
828         cmd->details.list_addresses.alh
829           = GNUNET_ATS_performance_list_addresses (perf_ats,
830                                                    &pid,
831                                                    cmd->details.list_addresses.all,
832                                                    &info_cb,
833                                                    cmd);
834         return;
835       }
836     case CMD_RESERVE_BANDWIDTH:
837       {
838         struct GNUNET_PeerIdentity pid;
839
840         make_peer (cmd->details.reserve_bandwidth.pid,
841                    &pid);
842         cmd->details.reserve_bandwidth.rc
843           = GNUNET_ATS_reserve_bandwidth (perf_ats,
844                                           &pid,
845                                           cmd->details.reserve_bandwidth.amount,
846                                           &reservation_cb,
847                                           cmd);
848         return;
849       }
850     } /* end switch */
851   } /* end while(1) */
852 }
853
854
855 /**
856  * Signature of a function called by ATS with the current bandwidth
857  * and address preferences as determined by ATS.
858  *
859  * @param cls closure, should point to "asc-closure"
860  * @param peer for which we suggest an address, NULL if ATS connection died
861  * @param address suggested address (including peer identity of the peer),
862  *             may be NULL to signal disconnect from peer
863  * @param session session to use, NULL to establish a new outgoing session
864  * @param bandwidth_out assigned outbound bandwidth for the connection,
865  *        0 to signal disconnect
866  * @param bandwidth_in assigned inbound bandwidth for the connection,
867  *        0 to signal disconnect
868  */
869 static void
870 address_suggest_cb (void *cls,
871                     const struct GNUNET_PeerIdentity *peer,
872                     const struct GNUNET_HELLO_Address *address,
873                     struct Session *session,
874                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
875                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
876 {
877   const char *asc_cls = cls;
878   struct AddressSuggestData *asd;
879
880   GNUNET_break (0 == strcmp (asc_cls, "asc-closure"));
881   if (NULL == peer)
882   {
883     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
884                 "Connection to ATS died, likely a crash!\n");
885     GNUNET_SCHEDULER_shutdown ();
886 #if 0
887     /* This is what we should do if we wanted to continue past
888        the ATS crash. */
889     GNUNET_CONTAINER_multipeermap_iterate (p2asd,
890                                            &free_asd,
891                                            NULL);
892     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
893                                            &free_aid,
894                                            NULL);
895 #endif
896     return;
897   }
898
899   asd = find_address_suggestion (peer);
900   if (NULL == asd)
901   {
902     asd = GNUNET_new (struct AddressSuggestData);
903     GNUNET_assert (GNUNET_YES ==
904                    GNUNET_CONTAINER_multipeermap_put (p2asd,
905                                                       peer,
906                                                       asd,
907                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
908   }
909   if ( (0 == ntohl (bandwidth_out.value__)) &&
910        (0 == ntohl (bandwidth_in.value__)) )
911     asd->active = GNUNET_NO;
912   else
913     asd->active = GNUNET_YES;
914   asd->bandwidth_out = bandwidth_out;
915   asd->bandwidth_in = bandwidth_in;
916   asd->session = session;
917   GNUNET_free_non_null (asd->address);
918   asd->address = NULL;
919   if (NULL != address)
920     asd->address = GNUNET_HELLO_address_copy (address);
921   run_interpreter ();
922 }
923
924
925 /**
926  * Signature of a function that is called with QoS information about an address.
927  *
928  * @param cls closure, should point to "aic-closure"
929  * @param address the address, NULL if ATS service was disconnected
930  * @param address_active #GNUNET_YES if this address is actively used
931  *        to maintain a connection to a peer;
932  *        #GNUNET_NO if the address is not actively used;
933  *        #GNUNET_SYSERR if this address is no longer available for ATS
934  * @param bandwidth_out assigned outbound bandwidth for the connection
935  * @param bandwidth_in assigned inbound bandwidth for the connection
936  * @param prop performance data for the address
937  */
938 static void
939 address_information_cb (void *cls,
940                         const struct GNUNET_HELLO_Address *address,
941                         int address_active,
942                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
943                         struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
944                         const struct GNUNET_ATS_Properties *prop)
945 {
946   const char *aic_cls = cls;
947   struct AddressInformationData *aid;
948
949   GNUNET_break (0 == strcmp (aic_cls, "aic-closure"));
950   if (NULL == address)
951   {
952     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
953                 "Connection to ATS died, likely a crash!\n");
954     GNUNET_CONTAINER_multipeermap_iterate (p2aid,
955                                            &free_aid,
956                                            NULL);
957     return;
958   }
959
960   aid = find_address_information (address);
961   if (NULL == aid)
962   {
963     aid = GNUNET_new (struct AddressInformationData);
964     aid->address = GNUNET_HELLO_address_copy (address);
965     GNUNET_assert (GNUNET_YES ==
966                    GNUNET_CONTAINER_multipeermap_put (p2aid,
967                                                       &address->peer,
968                                                       aid,
969                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
970   }
971   aid->active = address_active;
972   aid->bandwidth_out = bandwidth_out;
973   aid->bandwidth_in = bandwidth_in;
974   aid->properties = *prop;
975   run_interpreter ();
976 }
977
978
979 /**
980  * Function run once the ATS service has been started.
981  *
982  * @param cls NULL
983  * @param cfg configuration for the testcase
984  * @param peer handle to the peer
985  */
986 static void
987 run (void *cls,
988      const struct GNUNET_CONFIGURATION_Handle *cfg,
989      struct GNUNET_TESTING_Peer *peer)
990 {
991   p2asd = GNUNET_CONTAINER_multipeermap_create (128,
992                                                 GNUNET_NO);
993   p2aid = GNUNET_CONTAINER_multipeermap_create (128,
994                                                 GNUNET_NO);
995   GNUNET_SCHEDULER_add_delayed (TIMEOUT,
996                                 &end,
997                                 NULL);
998
999   sched_ats = GNUNET_ATS_scheduling_init (cfg,
1000                                           &address_suggest_cb,
1001                                           "asc-closure");
1002   if (NULL == sched_ats)
1003   {
1004     GNUNET_break (0);
1005     GNUNET_SCHEDULER_shutdown ();
1006     return;
1007   }
1008   con_ats = GNUNET_ATS_connectivity_init (cfg);
1009   if (NULL == con_ats)
1010   {
1011     GNUNET_break (0);
1012     GNUNET_SCHEDULER_shutdown ();
1013     return;
1014   }
1015   perf_ats = GNUNET_ATS_performance_init (cfg,
1016                                           &address_information_cb,
1017                                           "aic-closure");
1018   if (NULL == perf_ats)
1019   {
1020     GNUNET_break (0);
1021     GNUNET_SCHEDULER_shutdown ();
1022     return;
1023   }
1024   run_interpreter ();
1025 }
1026
1027
1028 /**
1029  * Run ATS test.
1030  *
1031  * @param argc length of @a argv
1032  * @param argv command line
1033  * @param cmds commands to run with the interpreter
1034  * @param timeout how long is the test allowed to take?
1035  * @return 0 on success
1036  */
1037 int
1038 TEST_ATS_run (int argc,
1039               char *argv[],
1040               struct Command *cmds,
1041               struct GNUNET_TIME_Relative timeout)
1042 {
1043   char *test_filename = GNUNET_strdup (argv[0]);
1044   char *sep;
1045   char *config_file;
1046   char *underscore;
1047
1048   test_commands = cmds;
1049   TIMEOUT = timeout;
1050   if (NULL != (sep = strstr (test_filename, ".exe")))
1051     sep[0] = '\0';
1052   underscore = strrchr (test_filename, (int) '_');
1053   GNUNET_assert (NULL != underscore);
1054   GNUNET_asprintf (&config_file,
1055                    "test_ats_api_%s.conf",
1056                    underscore + 1);
1057   ret = 2;
1058   if (0 != GNUNET_TESTING_peer_run ("test-ats-api",
1059                                     config_file,
1060                                     &run, NULL))
1061     ret = 1;
1062   GNUNET_free (test_filename);
1063   GNUNET_free (config_file);
1064   return ret;
1065 }
1066
1067 /* end of test_ats_lib.c */