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