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