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