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