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