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