use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / ats / ats_api_performance.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2010, 2011, 2016 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/ats_api_performance.c
22  * @brief automatic transport selection and outbound bandwidth determination
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "ats.h"
29
30
31 #define LOG(kind, ...) GNUNET_log_from (kind, "ats-performance-api", \
32                                         __VA_ARGS__)
33
34
35 /**
36  * Linked list of pending reservations.
37  */
38 struct GNUNET_ATS_ReservationContext
39 {
40   /**
41    * Kept in a DLL.
42    */
43   struct GNUNET_ATS_ReservationContext *next;
44
45   /**
46    * Kept in a DLL.
47    */
48   struct GNUNET_ATS_ReservationContext *prev;
49
50   /**
51    * Target peer.
52    */
53   struct GNUNET_PeerIdentity peer;
54
55   /**
56    * Desired reservation
57    */
58   int32_t size;
59
60   /**
61    * Function to call on result.
62    */
63   GNUNET_ATS_ReservationCallback rcb;
64
65   /**
66    * Closure for @e rcb
67    */
68   void *rcb_cls;
69
70   /**
71    * Do we need to undo this reservation if it succeeded?  Set to
72    * #GNUNET_YES if a reservation is cancelled.  (at that point, 'info'
73    * is also set to NULL; however, info will ALSO be NULL for the
74    * reservation context that is created to undo the original request,
75    * so 'info' being NULL cannot be used to check if undo is
76    * required).
77    */
78   int undo;
79 };
80
81
82 /**
83  * Linked list of pending reservations.
84  */
85 struct GNUNET_ATS_AddressListHandle
86 {
87   /**
88    * Kept in a DLL.
89    */
90   struct GNUNET_ATS_AddressListHandle *next;
91
92   /**
93    * Kept in a DLL.
94    */
95   struct GNUNET_ATS_AddressListHandle *prev;
96
97   /**
98    * Performance handle
99    */
100   struct GNUNET_ATS_PerformanceHandle *ph;
101
102   /**
103    * Callback
104    */
105   GNUNET_ATS_AddressInformationCallback cb;
106
107   /**
108    * Callback closure for @e cb
109    */
110   void *cb_cls;
111
112   /**
113    * Target peer.
114    */
115   struct GNUNET_PeerIdentity peer;
116
117   /**
118    * Return all or specific peer only
119    */
120   int all_peers;
121
122   /**
123    * Return all or used address only
124    */
125   int all_addresses;
126
127   /**
128    * Request multiplexing
129    */
130   uint32_t id;
131 };
132
133
134 /**
135  * ATS Handle to obtain and/or modify performance information.
136  */
137 struct GNUNET_ATS_PerformanceHandle
138 {
139   /**
140    * Our configuration.
141    */
142   const struct GNUNET_CONFIGURATION_Handle *cfg;
143
144   /**
145    * Callback to invoke when an address has performance changes.
146    */
147   GNUNET_ATS_AddressInformationCallback addr_info_cb;
148
149   /**
150    * Closure for @e addr_info_cb.
151    */
152   void *addr_info_cb_cls;
153
154   /**
155    * Connection to ATS service.
156    */
157   struct GNUNET_MQ_Handle *mq;
158
159   /**
160    * Head of linked list of pending reservation requests.
161    */
162   struct GNUNET_ATS_ReservationContext *reservation_head;
163
164   /**
165    * Tail of linked list of pending reservation requests.
166    */
167   struct GNUNET_ATS_ReservationContext *reservation_tail;
168
169   /**
170    * Head of linked list of pending address list requests.
171    */
172   struct GNUNET_ATS_AddressListHandle *addresslist_head;
173
174   /**
175    * Tail of linked list of pending address list requests.
176    */
177   struct GNUNET_ATS_AddressListHandle *addresslist_tail;
178
179   /**
180    * Current request for transmission to ATS.
181    */
182   struct GNUNET_CLIENT_TransmitHandle *th;
183
184   /**
185    * Task to trigger reconnect.
186    */
187   struct GNUNET_SCHEDULER_Task *task;
188
189   /**
190    * Reconnect backoff delay.
191    */
192   struct GNUNET_TIME_Relative backoff;
193
194   /**
195    * Monitor request multiplexing
196    */
197   uint32_t monitor_id;
198
199   /**
200    * Request multiplexing
201    */
202   uint32_t id;
203
204   /**
205    * Is the receive loop active?
206    */
207   int in_receive;
208 };
209
210 /**
211  * Re-establish the connection to the ATS service.
212  *
213  * @param ph handle to use to re-connect.
214  */
215 static void
216 reconnect (struct GNUNET_ATS_PerformanceHandle *ph);
217
218
219 /**
220  * Re-establish the connection to the ATS service.
221  *
222  * @param cls handle to use to re-connect.
223  */
224 static void
225 reconnect_task (void *cls)
226 {
227   struct GNUNET_ATS_PerformanceHandle *ph = cls;
228
229   ph->task = NULL;
230   reconnect (ph);
231 }
232
233
234 /**
235  * Reconnect to the ATS service, something went wrong.
236  *
237  * @param ph handle to reconnect
238  */
239 static void
240 do_reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
241 {
242   struct GNUNET_ATS_ReservationContext *rc;
243   struct GNUNET_ATS_AddressListHandle *alh;
244   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
245
246   if (NULL != ph->mq)
247   {
248     GNUNET_MQ_destroy (ph->mq);
249     ph->mq = NULL;
250   }
251   while (NULL != (rc = ph->reservation_head))
252   {
253     GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
254                                  ph->reservation_tail,
255                                  rc);
256     if (NULL != rc->rcb)
257       rc->rcb (rc->rcb_cls,
258                NULL,
259                0,
260                GNUNET_TIME_UNIT_FOREVER_REL);
261     GNUNET_free (rc);
262   }
263   bandwidth_zero.value__ = htonl (0);
264   while (NULL != (alh = ph->addresslist_head))
265   {
266     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
267                                  ph->addresslist_tail,
268                                  alh);
269     if (NULL != alh->cb)
270       alh->cb (alh->cb_cls,
271                NULL,
272                GNUNET_NO,
273                bandwidth_zero,
274                bandwidth_zero,
275                NULL);
276     GNUNET_free (alh);
277   }
278   if (NULL != ph->addr_info_cb)
279   {
280     /* Indicate reconnect */
281     ph->addr_info_cb (ph->addr_info_cb_cls,
282                       NULL,
283                       GNUNET_NO,
284                       bandwidth_zero,
285                       bandwidth_zero,
286                       NULL);
287   }
288   ph->backoff = GNUNET_TIME_STD_BACKOFF (ph->backoff);
289   ph->task = GNUNET_SCHEDULER_add_delayed (ph->backoff,
290                                            &reconnect_task,
291                                            ph);
292 }
293
294
295 /**
296  * We received a peer information message.  Validate and process it.
297  *
298  * @param cls our context with the callback
299  * @param pi the message
300  * @return #GNUNET_OK if the message was well-formed
301  */
302 static int
303 check_peer_information (void *cls,
304                         const struct PeerInformationMessage *pi)
305 {
306   const char *plugin_address;
307   const char *plugin_name;
308   uint16_t plugin_address_length;
309   uint16_t plugin_name_length;
310
311   plugin_address_length = ntohs (pi->address_length);
312   plugin_name_length = ntohs (pi->plugin_name_length);
313   plugin_address = (const char *) &pi[1];
314   plugin_name = &plugin_address[plugin_address_length];
315   if ((plugin_address_length + plugin_name_length
316        + sizeof(struct PeerInformationMessage) != ntohs (pi->header.size)) ||
317       (plugin_name[plugin_name_length - 1] != '\0'))
318   {
319     GNUNET_break (0);
320     return GNUNET_SYSERR;
321   }
322   return GNUNET_OK;
323 }
324
325
326 /**
327  * We received a peer information message.  Validate and process it.
328  *
329  * @param cls our context with the callback
330  * @param pi the message
331  * @return #GNUNET_OK if the message was well-formed
332  */
333 static void
334 handle_peer_information (void *cls,
335                          const struct PeerInformationMessage *pi)
336 {
337   struct GNUNET_ATS_PerformanceHandle *ph = cls;
338   const char *plugin_address;
339   const char *plugin_name;
340   struct GNUNET_HELLO_Address address;
341   uint16_t plugin_address_length;
342   int addr_active;
343   struct GNUNET_ATS_Properties prop;
344
345   if (NULL == ph->addr_info_cb)
346     return;
347   plugin_address_length = ntohs (pi->address_length);
348   addr_active = (int) ntohl (pi->address_active);
349   plugin_address = (const char *) &pi[1];
350   plugin_name = &plugin_address[plugin_address_length];
351
352   GNUNET_ATS_properties_ntoh (&prop,
353                               &pi->properties);
354   address.peer = pi->peer;
355   address.local_info = (enum GNUNET_HELLO_AddressInfo) ntohl (
356     pi->address_local_info);
357   address.address = plugin_address;
358   address.address_length = plugin_address_length;
359   address.transport_name = plugin_name;
360   ph->addr_info_cb (ph->addr_info_cb_cls,
361                     &address,
362                     addr_active,
363                     pi->bandwidth_out,
364                     pi->bandwidth_in,
365                     &prop);
366 }
367
368
369 /**
370  * We received a reservation result message.  Validate and process it.
371  *
372  * @param cls our context with the callback
373  * @param rr the message
374  */
375 static void
376 handle_reservation_result (void *cls,
377                            const struct ReservationResultMessage *rr)
378 {
379   struct GNUNET_ATS_PerformanceHandle *ph = cls;
380   struct GNUNET_ATS_ReservationContext *rc;
381   int32_t amount;
382
383   amount = ntohl (rr->amount);
384   rc = ph->reservation_head;
385   if (0 != GNUNET_memcmp (&rr->peer,
386                           &rc->peer))
387   {
388     GNUNET_break (0);
389     reconnect (ph);
390     return;
391   }
392   GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
393                                ph->reservation_tail,
394                                rc);
395   if ((0 == amount) ||
396       (NULL != rc->rcb))
397   {
398     /* tell client if not cancelled */
399     if (NULL != rc->rcb)
400       rc->rcb (rc->rcb_cls,
401                &rr->peer,
402                amount,
403                GNUNET_TIME_relative_ntoh (rr->res_delay));
404     GNUNET_free (rc);
405     return;
406   }
407   /* amount non-zero, but client cancelled, consider undo! */
408   if (GNUNET_YES != rc->undo)
409   {
410     GNUNET_free (rc);
411     return;   /* do not try to undo failed undos or negative amounts */
412   }
413   GNUNET_free (rc);
414   (void) GNUNET_ATS_reserve_bandwidth (ph,
415                                        &rr->peer,
416                                        -amount,
417                                        NULL, NULL);
418 }
419
420
421 /**
422  * We received a PeerInformationMessage.  Validate it.
423  *
424  * @param cls our context with the callback
425  * @param pi the message
426  * @return #GNUNET_OK if the message was well-formed
427  */
428 static int
429 check_address_list (void *cls,
430                     const struct PeerInformationMessage *pi)
431 {
432   const char *plugin_address;
433   const char *plugin_name;
434   uint16_t plugin_address_length;
435   uint16_t plugin_name_length;
436
437   plugin_address_length = ntohs (pi->address_length);
438   plugin_name_length = ntohs (pi->plugin_name_length);
439   plugin_address = (const char *) &pi[1];
440   plugin_name = &plugin_address[plugin_address_length];
441   if ((plugin_address_length + plugin_name_length
442        + sizeof(struct PeerInformationMessage) != ntohs (pi->header.size)) ||
443       (plugin_name[plugin_name_length - 1] != '\0'))
444   {
445     GNUNET_break (0);
446     return GNUNET_SYSERR;
447   }
448   return GNUNET_OK;
449 }
450
451
452 /**
453  * We received a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE.
454  * Process it.
455  *
456  * @param cls our context with the callback
457  * @param pi the message
458  */
459 static void
460 handle_address_list (void *cls,
461                      const struct PeerInformationMessage *pi)
462 {
463   struct GNUNET_ATS_PerformanceHandle *ph = cls;
464   struct GNUNET_ATS_AddressListHandle *alh;
465   struct GNUNET_ATS_AddressListHandle *next;
466   const char *plugin_address;
467   const char *plugin_name;
468   struct GNUNET_HELLO_Address address;
469   struct GNUNET_PeerIdentity allzeros;
470   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
471   struct GNUNET_ATS_Properties prop;
472   uint16_t plugin_address_length;
473   uint16_t plugin_name_length;
474   uint32_t active;
475   uint32_t id;
476
477   id = ntohl (pi->id);
478   active = ntohl (pi->address_active);
479   plugin_address_length = ntohs (pi->address_length);
480   plugin_name_length = ntohs (pi->plugin_name_length);
481   plugin_address = (const char *) &pi[1];
482   plugin_name = &plugin_address[plugin_address_length];
483   LOG (GNUNET_ERROR_TYPE_DEBUG,
484        "Received ATS_ADDRESSLIST_RESPONSE message for peer %s and plugin %s\n",
485        GNUNET_i2s (&pi->peer),
486        plugin_name);
487
488   next = ph->addresslist_head;
489   while (NULL != (alh = next))
490   {
491     next = alh->next;
492     if (alh->id == id)
493       break;
494   }
495   if (NULL == alh)
496     return; /* was canceled */
497
498   memset (&allzeros, '\0', sizeof(allzeros));
499   if ((0 == GNUNET_is_zero (&pi->peer)) &&
500       (0 == plugin_name_length) &&
501       (0 == plugin_address_length))
502   {
503     /* Done */
504     LOG (GNUNET_ERROR_TYPE_DEBUG,
505          "Received last message for ATS_ADDRESSLIST_RESPONSE\n");
506     bandwidth_zero.value__ = htonl (0);
507     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
508                                  ph->addresslist_tail,
509                                  alh);
510     if (NULL != alh->cb)
511       alh->cb (alh->cb_cls,
512                NULL,
513                GNUNET_NO,
514                bandwidth_zero,
515                bandwidth_zero,
516                NULL);
517     GNUNET_free (alh);
518     return;
519   }
520
521   address.peer = pi->peer;
522   address.address = plugin_address;
523   address.address_length = plugin_address_length;
524   address.transport_name = plugin_name;
525   if (((GNUNET_YES == alh->all_addresses) ||
526        (GNUNET_YES == active)) &&
527       (NULL != alh->cb))
528   {
529     GNUNET_ATS_properties_ntoh (&prop,
530                                 &pi->properties);
531     alh->cb (alh->cb_cls,
532              &address,
533              active,
534              pi->bandwidth_out,
535              pi->bandwidth_in,
536              &prop);
537   }
538 }
539
540
541 /**
542  * Generic error handler, called with the appropriate error code and
543  * the same closure specified at the creation of the message queue.
544  * Not every message queue implementation supports an error handler.
545  *
546  * @param cls closure with the `struct GNUNET_ATS_PerformanceHandle *`
547  * @param error error code
548  */
549 static void
550 mq_error_handler (void *cls,
551                   enum GNUNET_MQ_Error error)
552 {
553   struct GNUNET_ATS_PerformanceHandle *ph = cls;
554
555   do_reconnect (ph);
556 }
557
558
559 /**
560  * Re-establish the connection to the ATS service.
561  *
562  * @param ph handle to use to re-connect.
563  */
564 static void
565 reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
566 {
567   struct GNUNET_MQ_MessageHandler handlers[] = {
568     GNUNET_MQ_hd_var_size (peer_information,
569                            GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION,
570                            struct PeerInformationMessage,
571                            ph),
572     GNUNET_MQ_hd_fixed_size (reservation_result,
573                              GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT,
574                              struct ReservationResultMessage,
575                              ph),
576     GNUNET_MQ_hd_var_size (address_list,
577                            GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE,
578                            struct PeerInformationMessage,
579                            ph),
580     GNUNET_MQ_handler_end ()
581   };
582   struct GNUNET_MQ_Envelope *env;
583   struct ClientStartMessage *init;
584
585   GNUNET_assert (NULL == ph->mq);
586   ph->mq = GNUNET_CLIENT_connect (ph->cfg,
587                                   "ats",
588                                   handlers,
589                                   &mq_error_handler,
590                                   ph);
591   if (NULL == ph->mq)
592     return;
593   env = GNUNET_MQ_msg (init,
594                        GNUNET_MESSAGE_TYPE_ATS_START);
595   init->start_flag = htonl ((NULL == ph->addr_info_cb)
596                             ? START_FLAG_PERFORMANCE_NO_PIC
597                             : START_FLAG_PERFORMANCE_WITH_PIC);
598   GNUNET_MQ_send (ph->mq,
599                   env);
600 }
601
602
603 /**
604  * Get handle to access performance API of the ATS subsystem.
605  *
606  * @param cfg configuration to use
607  * @param addr_info_cb callback called when performance characteristics for
608  *      an address change
609  * @param addr_info_cb_cls closure for @a addr_info_cb
610  * @return ats performance context
611  */
612 struct GNUNET_ATS_PerformanceHandle *
613 GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
614                              GNUNET_ATS_AddressInformationCallback addr_info_cb,
615                              void *addr_info_cb_cls)
616 {
617   struct GNUNET_ATS_PerformanceHandle *ph;
618
619   ph = GNUNET_new (struct GNUNET_ATS_PerformanceHandle);
620   ph->cfg = cfg;
621   ph->addr_info_cb = addr_info_cb;
622   ph->addr_info_cb_cls = addr_info_cb_cls;
623   reconnect (ph);
624   if (NULL == ph->mq)
625   {
626     GNUNET_free (ph);
627     return NULL;
628   }
629   return ph;
630 }
631
632
633 /**
634  * Client is done using the ATS performance subsystem, release resources.
635  *
636  * @param ph handle
637  */
638 void
639 GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph)
640 {
641   struct GNUNET_ATS_ReservationContext *rc;
642   struct GNUNET_ATS_AddressListHandle *alh;
643
644   while (NULL != (alh = ph->addresslist_head))
645   {
646     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
647                                  ph->addresslist_tail,
648                                  alh);
649     GNUNET_free (alh);
650   }
651   while (NULL != (rc = ph->reservation_head))
652   {
653     GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
654                                  ph->reservation_tail,
655                                  rc);
656     GNUNET_break (NULL == rc->rcb);
657     GNUNET_free (rc);
658   }
659   if (NULL != ph->task)
660   {
661     GNUNET_SCHEDULER_cancel (ph->task);
662     ph->task = NULL;
663   }
664   if (NULL != ph->mq)
665   {
666     GNUNET_MQ_destroy (ph->mq);
667     ph->mq = NULL;
668   }
669   GNUNET_free (ph);
670 }
671
672
673 /**
674  * Reserve inbound bandwidth from the given peer.  ATS will look at
675  * the current amount of traffic we receive from the peer and ensure
676  * that the peer could add @a amount of data to its stream.
677  *
678  * @param ph performance handle
679  * @param peer identifies the peer
680  * @param amount reserve N bytes for receiving, negative
681  *                amounts can be used to undo a (recent) reservation;
682  * @param rcb function to call with the resulting reservation information
683  * @param rcb_cls closure for @a rcb
684  * @return NULL on error
685  * @deprecated will be replaced soon
686  */
687 struct GNUNET_ATS_ReservationContext *
688 GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph,
689                               const struct GNUNET_PeerIdentity *peer,
690                               int32_t amount,
691                               GNUNET_ATS_ReservationCallback rcb,
692                               void *rcb_cls)
693 {
694   struct GNUNET_ATS_ReservationContext *rc;
695   struct GNUNET_MQ_Envelope *env;
696   struct ReservationRequestMessage *m;
697
698   if (NULL == ph->mq)
699     return NULL;
700   rc = GNUNET_new (struct GNUNET_ATS_ReservationContext);
701   rc->size = amount;
702   rc->peer = *peer;
703   rc->rcb = rcb;
704   rc->rcb_cls = rcb_cls;
705   if ((NULL != rcb) &&
706       (amount > 0))
707     rc->undo = GNUNET_YES;
708   GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head,
709                                     ph->reservation_tail,
710                                     rc);
711   env = GNUNET_MQ_msg (m,
712                        GNUNET_MESSAGE_TYPE_ATS_RESERVATION_REQUEST);
713   m->amount = htonl (amount);
714   m->peer = *peer;
715   GNUNET_MQ_send (ph->mq,
716                   env);
717   return rc;
718 }
719
720
721 /**
722  * Cancel request for reserving bandwidth.
723  *
724  * @param rc context returned by the original #GNUNET_ATS_reserve_bandwidth() call
725  */
726 void
727 GNUNET_ATS_reserve_bandwidth_cancel (struct GNUNET_ATS_ReservationContext *rc)
728 {
729   rc->rcb = NULL;
730 }
731
732
733 /**
734  * Get information about addresses known to the ATS subsystem.
735  *
736  * @param ph the performance handle to use
737  * @param peer peer idm can be NULL for all peers
738  * @param all #GNUNET_YES to get information about all addresses or #GNUNET_NO to
739  *        get only address currently used
740  * @param infocb callback to call with the addresses,
741  *        will callback with address == NULL when done
742  * @param infocb_cls closure for @a infocb
743  * @return ats performance context
744  */
745 struct GNUNET_ATS_AddressListHandle*
746 GNUNET_ATS_performance_list_addresses (struct GNUNET_ATS_PerformanceHandle *ph,
747                                        const struct GNUNET_PeerIdentity *peer,
748                                        int all,
749                                        GNUNET_ATS_AddressInformationCallback
750                                        infocb,
751                                        void *infocb_cls)
752 {
753   struct GNUNET_ATS_AddressListHandle *alh;
754   struct GNUNET_MQ_Envelope *env;
755   struct AddressListRequestMessage *m;
756
757   if (NULL == ph->mq)
758     return NULL;
759   if (NULL == infocb)
760   {
761     GNUNET_break (0);
762     return NULL;
763   }
764   alh = GNUNET_new (struct GNUNET_ATS_AddressListHandle);
765   alh->id = ph->id++;
766   alh->cb = infocb;
767   alh->cb_cls = infocb_cls;
768   alh->ph = ph;
769   alh->all_addresses = all;
770   if (NULL == peer)
771   {
772     alh->all_peers = GNUNET_YES;
773   }
774   else
775   {
776     alh->all_peers = GNUNET_NO;
777     alh->peer = *peer;
778   }
779   GNUNET_CONTAINER_DLL_insert (ph->addresslist_head,
780                                ph->addresslist_tail,
781                                alh);
782   env = GNUNET_MQ_msg (m,
783                        GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST);
784   m->all = htonl (all);
785   m->id = htonl (alh->id);
786   if (NULL != peer)
787     m->peer = *peer;
788   GNUNET_MQ_send (ph->mq,
789                   env);
790   return alh;
791 }
792
793
794 /**
795  * Cancel a pending address listing operation
796  *
797  * @param alh the handle of the request to cancel
798  */
799 void
800 GNUNET_ATS_performance_list_addresses_cancel (struct
801                                               GNUNET_ATS_AddressListHandle *alh)
802 {
803   struct GNUNET_ATS_PerformanceHandle *ph = alh->ph;
804
805   GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
806                                ph->addresslist_tail,
807                                alh);
808   GNUNET_free (alh);
809 }
810
811
812 /**
813  * Convert a `enum GNUNET_ATS_PreferenceType` to a string
814  *
815  * @param type the preference type
816  * @return a string or NULL if invalid
817  */
818 const char *
819 GNUNET_ATS_print_preference_type (enum GNUNET_ATS_PreferenceKind type)
820 {
821   const char *prefs[] = GNUNET_ATS_PreferenceTypeString;
822
823   if (type < GNUNET_ATS_PREFERENCE_END)
824     return prefs[type];
825   return NULL;
826 }
827
828
829 /**
830  * Change preferences for the given peer. Preference changes are forgotten if peers
831  * disconnect.
832  *
833  * @param ph performance handle
834  * @param peer identifies the peer
835  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
836  */
837 void
838 GNUNET_ATS_performance_change_preference (struct
839                                           GNUNET_ATS_PerformanceHandle *ph,
840                                           const struct
841                                           GNUNET_PeerIdentity *peer,
842                                           ...)
843 {
844   struct GNUNET_MQ_Envelope *env;
845   struct ChangePreferenceMessage *m;
846   uint32_t count;
847   struct PreferenceInformation *pi;
848   va_list ap;
849   enum GNUNET_ATS_PreferenceKind kind;
850
851   if (NULL == ph->mq)
852     return;
853   count = 0;
854   va_start (ap, peer);
855   while (GNUNET_ATS_PREFERENCE_END !=
856          (kind = GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind)))
857   {
858     switch (kind)
859     {
860     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
861       count++;
862       (void) va_arg (ap, double);
863       break;
864
865     case GNUNET_ATS_PREFERENCE_LATENCY:
866       count++;
867       (void) va_arg (ap, double);
868       break;
869
870     default:
871       GNUNET_assert (0);
872     }
873   }
874   va_end (ap);
875   env = GNUNET_MQ_msg_extra (m,
876                              count * sizeof(struct PreferenceInformation),
877                              GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE);
878   m->num_preferences = htonl (count);
879   m->peer = *peer;
880   pi = (struct PreferenceInformation *) &m[1];
881   count = 0;
882   va_start (ap, peer);
883   while (GNUNET_ATS_PREFERENCE_END != (kind =
884                                          GNUNET_VA_ARG_ENUM (ap,
885                                                              GNUNET_ATS_PreferenceKind)))
886   {
887     pi[count].preference_kind = htonl (kind);
888     switch (kind)
889     {
890     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
891       pi[count].preference_value = (float) va_arg (ap, double);
892
893       count++;
894       break;
895
896     case GNUNET_ATS_PREFERENCE_LATENCY:
897       pi[count].preference_value = (float) va_arg (ap, double);
898
899       count++;
900       break;
901
902     default:
903       GNUNET_assert (0);
904     }
905   }
906   va_end (ap);
907   GNUNET_MQ_send (ph->mq,
908                   env);
909 }
910
911
912 /**
913  * Send feedback to ATS on how good a the requirements for a peer and a
914  * preference is satisfied by ATS
915  *
916  * @param ph performance handle
917  * @param scope the time interval this valid for: [now - scope .. now]
918  * @param peer identifies the peer
919  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
920  */
921 void
922 GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph,
923                                       const struct GNUNET_PeerIdentity *peer,
924                                       const struct GNUNET_TIME_Relative scope,
925                                       ...)
926 {
927   struct GNUNET_MQ_Envelope *env;
928   struct FeedbackPreferenceMessage *m;
929   uint32_t count;
930   struct PreferenceInformation *pi;
931   va_list ap;
932   enum GNUNET_ATS_PreferenceKind kind;
933
934   if (NULL == ph->mq)
935     return;
936   count = 0;
937   va_start (ap, scope);
938   while (GNUNET_ATS_PREFERENCE_END !=
939          (kind = GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind)))
940   {
941     switch (kind)
942     {
943     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
944       count++;
945       (void) va_arg (ap, double);
946       break;
947
948     case GNUNET_ATS_PREFERENCE_LATENCY:
949       count++;
950       (void) va_arg (ap, double);
951       break;
952
953     default:
954       GNUNET_assert (0);
955     }
956   }
957   va_end (ap);
958   env = GNUNET_MQ_msg_extra (m,
959                              count * sizeof(struct PreferenceInformation),
960                              GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK);
961   m->scope = GNUNET_TIME_relative_hton (scope);
962   m->num_feedback = htonl (count);
963   m->peer = *peer;
964   pi = (struct PreferenceInformation *) &m[1];
965   count = 0;
966   va_start (ap, scope);
967   while (GNUNET_ATS_PREFERENCE_END != (kind =
968                                          GNUNET_VA_ARG_ENUM (ap,
969                                                              GNUNET_ATS_PreferenceKind)))
970   {
971     pi[count].preference_kind = htonl (kind);
972     switch (kind)
973     {
974     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
975       pi[count].preference_value = (float) va_arg (ap, double);
976
977       count++;
978       break;
979
980     case GNUNET_ATS_PREFERENCE_LATENCY:
981       pi[count].preference_value = (float) va_arg (ap, double);
982
983       count++;
984       break;
985
986     default:
987       GNUNET_assert (0);
988     }
989   }
990   va_end (ap);
991   GNUNET_MQ_send (ph->mq,
992                   env);
993 }
994
995
996 /* end of ats_api_performance.c */