Merge branch 'master' of ssh://gnunet.org/gnunet
[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 /**
19  * @file ats/ats_api_performance.c
20  * @brief automatic transport selection and outbound bandwidth determination
21  * @author Christian Grothoff
22  * @author Matthias Wachs
23  */
24 #include "platform.h"
25 #include "gnunet_ats_service.h"
26 #include "ats.h"
27
28
29 #define LOG(kind,...) GNUNET_log_from(kind, "ats-performance-api", __VA_ARGS__)
30
31
32 /**
33  * Linked list of pending reservations.
34  */
35 struct GNUNET_ATS_ReservationContext
36 {
37
38   /**
39    * Kept in a DLL.
40    */
41   struct GNUNET_ATS_ReservationContext *next;
42
43   /**
44    * Kept in a DLL.
45    */
46   struct GNUNET_ATS_ReservationContext *prev;
47
48   /**
49    * Target peer.
50    */
51   struct GNUNET_PeerIdentity peer;
52
53   /**
54    * Desired reservation
55    */
56   int32_t size;
57
58   /**
59    * Function to call on result.
60    */
61   GNUNET_ATS_ReservationCallback rcb;
62
63   /**
64    * Closure for @e rcb
65    */
66   void *rcb_cls;
67
68   /**
69    * Do we need to undo this reservation if it succeeded?  Set to
70    * #GNUNET_YES if a reservation is cancelled.  (at that point, 'info'
71    * is also set to NULL; however, info will ALSO be NULL for the
72    * reservation context that is created to undo the original request,
73    * so 'info' being NULL cannot be used to check if undo is
74    * required).
75    */
76   int undo;
77 };
78
79
80 /**
81  * Linked list of pending reservations.
82  */
83 struct GNUNET_ATS_AddressListHandle
84 {
85
86   /**
87    * Kept in a DLL.
88    */
89   struct GNUNET_ATS_AddressListHandle *next;
90
91   /**
92    * Kept in a DLL.
93    */
94   struct GNUNET_ATS_AddressListHandle *prev;
95
96   /**
97    * Performance handle
98    */
99   struct GNUNET_ATS_PerformanceHandle *ph;
100
101   /**
102    * Callback
103    */
104   GNUNET_ATS_AddressInformationCallback cb;
105
106   /**
107    * Callback closure for @e cb
108    */
109   void *cb_cls;
110
111   /**
112    * Target peer.
113    */
114   struct GNUNET_PeerIdentity peer;
115
116   /**
117    * Return all or specific peer only
118    */
119   int all_peers;
120
121   /**
122    * Return all or used address only
123    */
124   int all_addresses;
125
126   /**
127    * Request multiplexing
128    */
129   uint32_t id;
130 };
131
132
133 /**
134  * ATS Handle to obtain and/or modify performance information.
135  */
136 struct GNUNET_ATS_PerformanceHandle
137 {
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 (pi->address_local_info);
356   address.address = plugin_address;
357   address.address_length = plugin_address_length;
358   address.transport_name = plugin_name;
359   ph->addr_info_cb (ph->addr_info_cb_cls,
360                     &address,
361                     addr_active,
362                     pi->bandwidth_out,
363                     pi->bandwidth_in,
364                     &prop);
365 }
366
367
368 /**
369  * We received a reservation result message.  Validate and process it.
370  *
371  * @param cls our context with the callback
372  * @param rr the message
373  */
374 static void
375 handle_reservation_result (void *cls,
376                            const struct ReservationResultMessage *rr)
377 {
378   struct GNUNET_ATS_PerformanceHandle *ph = cls;
379   struct GNUNET_ATS_ReservationContext *rc;
380   int32_t amount;
381
382   amount = ntohl (rr->amount);
383   rc = ph->reservation_head;
384   if (0 != memcmp (&rr->peer,
385                    &rc->peer,
386                    sizeof(struct GNUNET_PeerIdentity)))
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 == memcmp (&allzeros, &pi->peer, sizeof(allzeros))) &&
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 infocb,
750                                        void *infocb_cls)
751 {
752   struct GNUNET_ATS_AddressListHandle *alh;
753   struct GNUNET_MQ_Envelope *env;
754   struct AddressListRequestMessage *m;
755
756   if (NULL == ph->mq)
757     return NULL;
758   if (NULL == infocb)
759   {
760     GNUNET_break (0);
761     return NULL;
762   }
763   alh = GNUNET_new (struct GNUNET_ATS_AddressListHandle);
764   alh->id = ph->id++;
765   alh->cb = infocb;
766   alh->cb_cls = infocb_cls;
767   alh->ph = ph;
768   alh->all_addresses = all;
769   if (NULL == peer)
770   {
771     alh->all_peers = GNUNET_YES;
772   }
773   else
774   {
775     alh->all_peers = GNUNET_NO;
776     alh->peer = *peer;
777   }
778   GNUNET_CONTAINER_DLL_insert (ph->addresslist_head,
779                                ph->addresslist_tail,
780                                alh);
781   env = GNUNET_MQ_msg (m,
782                        GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST);
783   m->all = htonl (all);
784   m->id = htonl (alh->id);
785   if (NULL != peer)
786     m->peer = *peer;
787   GNUNET_MQ_send (ph->mq,
788                   env);
789   return alh;
790 }
791
792
793 /**
794  * Cancel a pending address listing operation
795  *
796  * @param alh the handle of the request to cancel
797  */
798 void
799 GNUNET_ATS_performance_list_addresses_cancel (struct GNUNET_ATS_AddressListHandle *alh)
800 {
801   struct GNUNET_ATS_PerformanceHandle *ph = alh->ph;
802
803   GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
804                                ph->addresslist_tail,
805                                alh);
806   GNUNET_free (alh);
807 }
808
809
810 /**
811  * Convert a `enum GNUNET_ATS_PreferenceType` to a string
812  *
813  * @param type the preference type
814  * @return a string or NULL if invalid
815  */
816 const char *
817 GNUNET_ATS_print_preference_type (enum GNUNET_ATS_PreferenceKind type)
818 {
819   const char *prefs[] = GNUNET_ATS_PreferenceTypeString;
820
821   if (type < GNUNET_ATS_PREFERENCE_END)
822     return prefs[type];
823   return NULL;
824 }
825
826
827 /**
828  * Change preferences for the given peer. Preference changes are forgotten if peers
829  * disconnect.
830  *
831  * @param ph performance handle
832  * @param peer identifies the peer
833  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
834  */
835 void
836 GNUNET_ATS_performance_change_preference (struct GNUNET_ATS_PerformanceHandle *ph,
837                                           const struct GNUNET_PeerIdentity *peer,
838                                           ...)
839 {
840   struct GNUNET_MQ_Envelope *env;
841   struct ChangePreferenceMessage *m;
842   uint32_t count;
843   struct PreferenceInformation *pi;
844   va_list ap;
845   enum GNUNET_ATS_PreferenceKind kind;
846
847   if (NULL == ph->mq)
848     return;
849   count = 0;
850   va_start(ap, peer);
851   while (GNUNET_ATS_PREFERENCE_END !=
852          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
853   {
854     switch (kind)
855     {
856     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
857       count++;
858       (void) va_arg (ap, double);
859       break;
860     case GNUNET_ATS_PREFERENCE_LATENCY:
861       count++;
862       (void) va_arg (ap, double);
863       break;
864     default:
865       GNUNET_assert(0);
866     }
867   }
868   va_end(ap);
869   env = GNUNET_MQ_msg_extra (m,
870                              count * sizeof(struct PreferenceInformation),
871                              GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE);
872   m->num_preferences = htonl (count);
873   m->peer = *peer;
874   pi = (struct PreferenceInformation *) &m[1];
875   count = 0;
876   va_start(ap, peer);
877   while (GNUNET_ATS_PREFERENCE_END != (kind =
878       va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
879   {
880     pi[count].preference_kind = htonl (kind);
881     switch (kind)
882     {
883     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
884       pi[count].preference_value = (float) va_arg (ap, double);
885
886       count++;
887       break;
888     case GNUNET_ATS_PREFERENCE_LATENCY:
889       pi[count].preference_value = (float) va_arg (ap, double);
890
891       count++;
892       break;
893     default:
894       GNUNET_assert(0);
895     }
896   }
897   va_end(ap);
898   GNUNET_MQ_send (ph->mq,
899                   env);
900 }
901
902
903 /**
904  * Send feedback to ATS on how good a the requirements for a peer and a
905  * preference is satisfied by ATS
906  *
907  * @param ph performance handle
908  * @param scope the time interval this valid for: [now - scope .. now]
909  * @param peer identifies the peer
910  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
911  */
912 void
913 GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph,
914                                       const struct GNUNET_PeerIdentity *peer,
915                                       const struct GNUNET_TIME_Relative scope,
916                                       ...)
917 {
918   struct GNUNET_MQ_Envelope *env;
919   struct FeedbackPreferenceMessage *m;
920   uint32_t count;
921   struct PreferenceInformation *pi;
922   va_list ap;
923   enum GNUNET_ATS_PreferenceKind kind;
924
925   if (NULL == ph->mq)
926     return;
927   count = 0;
928   va_start(ap, scope);
929   while (GNUNET_ATS_PREFERENCE_END !=
930          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
931   {
932     switch (kind)
933     {
934     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
935       count++;
936       (void) va_arg (ap, double);
937       break;
938     case GNUNET_ATS_PREFERENCE_LATENCY:
939       count++;
940       (void) va_arg (ap, double);
941       break;
942     default:
943       GNUNET_assert(0);
944     }
945   }
946   va_end(ap);
947   env = GNUNET_MQ_msg_extra (m,
948                              count * sizeof(struct PreferenceInformation),
949                              GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK);
950   m->scope = GNUNET_TIME_relative_hton (scope);
951   m->num_feedback = htonl (count);
952   m->peer = *peer;
953   pi = (struct PreferenceInformation *) &m[1];
954   count = 0;
955   va_start(ap, scope);
956   while (GNUNET_ATS_PREFERENCE_END != (kind =
957       va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
958   {
959     pi[count].preference_kind = htonl (kind);
960     switch (kind)
961     {
962     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
963       pi[count].preference_value = (float) va_arg (ap, double);
964
965       count++;
966       break;
967     case GNUNET_ATS_PREFERENCE_LATENCY:
968       pi[count].preference_value = (float) va_arg (ap, double);
969
970       count++;
971       break;
972     default:
973       GNUNET_assert(0);
974     }
975   }
976   va_end(ap);
977   GNUNET_MQ_send (ph->mq,
978                   env);
979 }
980
981 /* end of ats_api_performance.c */