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