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