fix warning
[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     GNUNET_free (p);
312   }
313   do_transmit (ph);
314   return ret;
315 }
316
317
318 /**
319  * Transmit messages from the message queue to the service
320  * (if there are any, and if we are not already trying).
321  *
322  * @param ph handle to use
323  */
324 static void
325 do_transmit (struct GNUNET_ATS_PerformanceHandle *ph)
326 {
327   struct PendingMessage *p;
328
329   if (NULL != ph->th)
330     return;
331   if (NULL == (p = ph->pending_head))
332     return;
333   if (NULL == ph->client)
334     return;                     /* currently reconnecting */
335   ph->th =
336       GNUNET_CLIENT_notify_transmit_ready (ph->client, p->size,
337                                            GNUNET_TIME_UNIT_FOREVER_REL,
338                                            GNUNET_YES, &transmit_message_to_ats,
339                                            ph);
340 }
341
342
343 /**
344  * We received a peer information message.  Validate and process it.
345  *
346  * @param ph our context with the callback
347  * @param msg the message
348  * @return GNUNET_OK if the message was well-formed
349  */
350 static int
351 process_pi_message (struct GNUNET_ATS_PerformanceHandle *ph,
352                     const struct GNUNET_MessageHeader *msg)
353 {
354   const struct PeerInformationMessage *pi;
355   const struct GNUNET_ATS_Information *atsi;
356   const char *plugin_address;
357   const char *plugin_name;
358   struct GNUNET_HELLO_Address address;
359   uint16_t plugin_address_length;
360   uint16_t plugin_name_length;
361   uint32_t ats_count;
362   int addr_active;
363
364   if (ntohs (msg->size) < sizeof (struct PeerInformationMessage))
365   {
366     GNUNET_break (0);
367     return GNUNET_SYSERR;
368   }
369
370   pi = (const struct PeerInformationMessage *) msg;
371   ats_count = ntohl (pi->ats_count);
372   plugin_address_length = ntohs (pi->address_length);
373   plugin_name_length = ntohs (pi->plugin_name_length);
374   addr_active = ntohl (pi->address_active);
375   atsi = (const struct GNUNET_ATS_Information *) &pi[1];
376   plugin_address = (const char *) &atsi[ats_count];
377   plugin_name = &plugin_address[plugin_address_length];
378   if ((plugin_address_length + plugin_name_length +
379        ats_count * sizeof (struct GNUNET_ATS_Information) +
380        sizeof (struct PeerInformationMessage) != ntohs (msg->size)) ||
381       (ats_count >
382        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
383       || (plugin_name[plugin_name_length - 1] != '\0'))
384   {
385     GNUNET_break (0);
386     return GNUNET_SYSERR;
387   }
388
389   if (NULL != ph->addr_info_cb)
390   {
391           address.peer = pi->peer;
392           address.address = plugin_address;
393           address.address_length = plugin_address_length;
394           address.transport_name = plugin_name;
395
396                 ph->addr_info_cb (ph->addr_info_cb_cls, &address, addr_active, pi->bandwidth_out, 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"), "ATS_ADDRESSLIST_RESPONSE");
486
487   pi = (const struct PeerInformationMessage *) msg;
488   id = ntohl (pi->id);
489   ats_count = ntohl (pi->ats_count);
490   active = ntohl (pi->address_active);
491   plugin_address_length = ntohs (pi->address_length);
492   plugin_name_length = ntohs (pi->plugin_name_length);
493   atsi = (const struct GNUNET_ATS_Information *) &pi[1];
494   plugin_address = (const char *) &atsi[ats_count];
495   plugin_name = &plugin_address[plugin_address_length];
496   if ((plugin_address_length + plugin_name_length +
497        ats_count * sizeof (struct GNUNET_ATS_Information) +
498        sizeof (struct PeerInformationMessage) != ntohs (msg->size)) ||
499       (ats_count >
500        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
501       || (plugin_name[plugin_name_length - 1] != '\0'))
502   {
503     GNUNET_break (0);
504     return GNUNET_SYSERR;
505   }
506
507   next = ph->addresslist_head;
508   while (NULL != (alh = next))
509   {
510       next = alh->next;
511       if (alh->id == id)
512         break;
513   }
514   if (NULL == alh)
515   {
516       /* was canceled */
517       return GNUNET_SYSERR;
518   }
519
520   memset (&allzeros, '\0', sizeof (allzeros));
521   if ((0 == memcmp (&allzeros, &pi->peer, sizeof (allzeros))) &&
522       (0 == plugin_name_length) &&
523       (0 == plugin_address_length) &&
524       (0 == ats_count))
525   {
526       /* Done */
527       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528           _("Received last message for %s \n"), "ATS_ADDRESSLIST_RESPONSE");
529       bandwidth_zero.value__ = htonl (0);
530       if (NULL != alh->cb)
531         alh->cb (ph->addr_info_cb_cls,
532               NULL,
533               GNUNET_NO,
534               bandwidth_zero, bandwidth_zero,
535               NULL, 0);
536       GNUNET_CONTAINER_DLL_remove (ph->addresslist_head, ph->addresslist_tail, alh);
537       GNUNET_free (alh);
538       return GNUNET_OK;
539   }
540
541   address.peer = pi->peer;
542   address.address = plugin_address;
543   address.address_length = plugin_address_length;
544   address.transport_name = plugin_name;
545
546   if ((GNUNET_YES == alh->all_addresses) || (GNUNET_YES == active))
547   {
548     if (NULL != alh->cb)
549       alh->cb (ph->addr_info_cb_cls,
550             &address,
551             active,
552             pi->bandwidth_out, pi->bandwidth_in,
553             atsi, ats_count);
554   }
555   return GNUNET_OK;
556 }
557
558
559 /**
560  * Type of a function to call when we receive a message
561  * from the service.
562  *
563  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
564  * @param msg message received, NULL on timeout or fatal error
565  */
566 static void
567 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
568 {
569   struct GNUNET_ATS_PerformanceHandle *ph = cls;
570
571   if (NULL == msg)
572     goto reconnect;
573   switch (ntohs (msg->type))
574   {
575   case GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION:
576     if (GNUNET_OK != process_pi_message (ph, msg))
577       goto reconnect;
578     break;
579   case GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT:
580     if (GNUNET_OK != process_rr_message (ph, msg))
581       goto reconnect;
582     break;
583   case GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE:
584     if (GNUNET_OK != process_ar_message (ph, msg))
585       goto reconnect;
586     break;
587   default:
588     GNUNET_break (0);
589     goto reconnect;
590   }
591   GNUNET_CLIENT_receive (ph->client, &process_ats_message, ph,
592                          GNUNET_TIME_UNIT_FOREVER_REL);
593   return;
594 reconnect:
595   if (NULL != ph->th)
596   {
597     GNUNET_CLIENT_notify_transmit_ready_cancel (ph->th);
598     ph->th = NULL;
599   }
600   GNUNET_CLIENT_disconnect (ph->client);
601   ph->client = NULL;
602   ph->task =
603       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
604                                     ph);
605 }
606
607
608 /**
609  * Re-establish the connection to the ATS service.
610  *
611  * @param ph handle to use to re-connect.
612  */
613 static void
614 reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
615 {
616   struct PendingMessage *p;
617   struct ClientStartMessage *init;
618
619   GNUNET_assert (NULL == ph->client);
620   ph->client = GNUNET_CLIENT_connect ("ats", ph->cfg);
621   GNUNET_assert (NULL != ph->client);
622   GNUNET_CLIENT_receive (ph->client, &process_ats_message, ph,
623                           GNUNET_TIME_UNIT_FOREVER_REL);
624   if ((NULL == (p = ph->pending_head)) || (GNUNET_YES != p->is_init))
625   {
626     p = GNUNET_malloc (sizeof (struct PendingMessage) +
627                        sizeof (struct ClientStartMessage));
628     p->size = sizeof (struct ClientStartMessage);
629     p->is_init = GNUNET_YES;
630     init = (struct ClientStartMessage *) &p[1];
631     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
632     init->header.size = htons (sizeof (struct ClientStartMessage));
633     init->start_flag =
634         htonl ((NULL ==ph->addr_info_cb) ?
635                         START_FLAG_PERFORMANCE_NO_PIC : START_FLAG_PERFORMANCE_WITH_PIC);
636     GNUNET_CONTAINER_DLL_insert (ph->pending_head, ph->pending_tail, p);
637   }
638   do_transmit (ph);
639 }
640
641
642
643 /**
644  * Get handle to access performance API of the ATS subsystem.
645  *
646  * @param cfg configuration to use
647  * @param addr_info_cb callback called when performance characteristics for
648  *      an address change
649  * @param addr_info_cb_cls closure for infocb
650  * @return ats performance context
651  */
652 struct GNUNET_ATS_PerformanceHandle *
653 GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
654                              GNUNET_ATS_AddressInformationCallback addr_info_cb,
655                              void *addr_info_cb_cls)
656 {
657   struct GNUNET_ATS_PerformanceHandle *ph;
658
659   ph = GNUNET_malloc (sizeof (struct GNUNET_ATS_PerformanceHandle));
660   ph->cfg = cfg;
661   ph->addr_info_cb = addr_info_cb;
662   ph->addr_info_cb_cls = addr_info_cb_cls;
663   ph->id  = 0;
664   reconnect (ph);
665   return ph;
666 }
667
668 /**
669  * Client is done using the ATS performance subsystem, release resources.
670  *
671  * @param ph handle
672  */
673 void
674 GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph)
675 {
676   struct PendingMessage *p;
677   struct GNUNET_ATS_ReservationContext *rc;
678   struct GNUNET_ATS_AddressListHandle *alh;
679
680   while (NULL != (p = ph->pending_head))
681   {
682     GNUNET_CONTAINER_DLL_remove (ph->pending_head, ph->pending_tail, p);
683     GNUNET_free (p);
684   }
685   while (NULL != (alh = ph->addresslist_head))
686   {
687     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head, ph->addresslist_tail,
688                                  alh);
689     GNUNET_free (alh);
690   }
691   while (NULL != (rc = ph->reservation_head))
692   {
693     GNUNET_CONTAINER_DLL_remove (ph->reservation_head, ph->reservation_tail,
694                                  rc);
695     GNUNET_break (NULL == rc->rcb);
696     GNUNET_free (rc);
697   }
698
699   if (GNUNET_SCHEDULER_NO_TASK != ph->task)
700   {
701     GNUNET_SCHEDULER_cancel (ph->task);
702     ph->task = GNUNET_SCHEDULER_NO_TASK;
703   }
704   if (NULL != ph->client)
705   {
706     GNUNET_CLIENT_disconnect (ph->client);
707     ph->client = NULL;
708   }
709   GNUNET_free (ph);
710 }
711
712
713 /**
714  * Reserve inbound bandwidth from the given peer.  ATS will look at
715  * the current amount of traffic we receive from the peer and ensure
716  * that the peer could add 'amount' of data to its stream.
717  *
718  * @param ph performance handle
719  * @param peer identifies the peer
720  * @param amount reserve N bytes for receiving, negative
721  *                amounts can be used to undo a (recent) reservation;
722  * @param rcb function to call with the resulting reservation information
723  * @param rcb_cls closure for info
724  * @return NULL on error
725  * @deprecated will be replaced soon
726  */
727 struct GNUNET_ATS_ReservationContext *
728 GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph,
729                               const struct GNUNET_PeerIdentity *peer,
730                               int32_t amount,
731                               GNUNET_ATS_ReservationCallback rcb, void *rcb_cls)
732 {
733   struct GNUNET_ATS_ReservationContext *rc;
734   struct PendingMessage *p;
735   struct ReservationRequestMessage *m;
736
737   rc = GNUNET_malloc (sizeof (struct GNUNET_ATS_ReservationContext));
738   rc->size = amount;
739   rc->peer = *peer;
740   rc->rcb = rcb;
741   rc->rcb_cls = rcb_cls;
742   if ((rcb != NULL) && (amount > 0))
743     rc->undo = GNUNET_YES;
744   GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head, ph->reservation_tail,
745                                     rc);
746
747   p = GNUNET_malloc (sizeof (struct PendingMessage) +
748                      sizeof (struct ReservationRequestMessage));
749   p->size = sizeof (struct ReservationRequestMessage);
750   p->is_init = GNUNET_NO;
751   m = (struct ReservationRequestMessage *) &p[1];
752   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESERVATION_REQUEST);
753   m->header.size = htons (sizeof (struct ReservationRequestMessage));
754   m->amount = htonl (amount);
755   m->peer = *peer;
756   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head, ph->pending_tail, p);
757   do_transmit (ph);
758   return rc;
759 }
760
761
762 /**
763  * Cancel request for reserving bandwidth.
764  *
765  * @param rc context returned by the original GNUNET_ATS_reserve_bandwidth call
766  */
767 void
768 GNUNET_ATS_reserve_bandwidth_cancel (struct GNUNET_ATS_ReservationContext *rc)
769 {
770   rc->rcb = NULL;
771 }
772
773 /**
774  * Get information about addresses known to the ATS subsystem.
775  *
776  * @param handle the performance handle to use
777  * @param peer peer idm can be NULL for all peers
778  * @param all GNUNET_YES to get information about all addresses or GNUNET_NO to
779  *        get only address currently used
780  * @param infocb callback to call with the addresses,
781  *        will callback with address == NULL when done
782  * @param infocb_cls closure for infocb
783  * @return ats performance context
784  */
785 struct GNUNET_ATS_AddressListHandle*
786 GNUNET_ATS_performance_list_addresses (struct GNUNET_ATS_PerformanceHandle *handle,
787                                        const struct GNUNET_PeerIdentity *peer,
788                                        int all,
789                                        GNUNET_ATS_AddressInformationCallback infocb,
790                                        void *infocb_cls)
791 {
792   struct GNUNET_ATS_AddressListHandle *alh;
793   struct PendingMessage *p;
794   struct AddressListRequestMessage *m;
795
796   GNUNET_assert (NULL != handle);
797   if (NULL == infocb)
798     return NULL;
799
800   alh = GNUNET_malloc (sizeof (struct GNUNET_ATS_AddressListHandle));
801   alh->id = handle->id;
802   handle->id ++;
803   alh->cb = infocb;
804   alh->cb_cls = infocb_cls;
805   alh->ph = handle;
806   alh->all_addresses = all;
807   if (NULL == peer)
808     alh->all_peers = GNUNET_YES;
809   else
810   {
811       alh->all_peers = GNUNET_NO;
812       alh->peer = (*peer);
813   }
814
815   GNUNET_CONTAINER_DLL_insert (handle->addresslist_head, handle->addresslist_tail, alh);
816
817   p = GNUNET_malloc (sizeof (struct PendingMessage) +
818                      sizeof (struct AddressListRequestMessage));
819   p->size = sizeof (struct AddressListRequestMessage);
820   m = (struct AddressListRequestMessage *) &p[1];
821   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST);
822   m->header.size = htons (sizeof (struct AddressListRequestMessage));
823   m->all = htonl (all);
824   m->id = htonl (alh->id);
825   if (NULL != peer)
826     m->peer = *peer;
827   else
828   {
829       memset (&m->peer, '\0', sizeof (struct GNUNET_PeerIdentity));
830   }
831   GNUNET_CONTAINER_DLL_insert_tail (handle->pending_head, handle->pending_tail, p);
832
833   do_transmit (handle);
834
835   return alh;
836 }
837
838
839 /**
840  * Cancel a pending address listing operation
841  *
842  * @param handle the GNUNET_ATS_AddressListHandle handle to cancel
843  */
844 void
845 GNUNET_ATS_performance_list_addresses_cancel (struct GNUNET_ATS_AddressListHandle *handle)
846 {
847   GNUNET_assert (NULL != handle);
848
849   GNUNET_CONTAINER_DLL_remove (handle->ph->addresslist_head, handle->ph->addresslist_tail, handle);
850   GNUNET_free (handle);
851 }
852
853
854 /**
855  * Convert a GNUNET_ATS_PreferenceType to a string
856  *
857  * @param type the preference type
858  * @return a string or NULL if invalid
859  */
860 const char *
861 GNUNET_ATS_print_preference_type (uint32_t type)
862 {
863   char *prefs[GNUNET_ATS_PreferenceCount] = GNUNET_ATS_PreferenceTypeString;
864   if (type < GNUNET_ATS_PreferenceCount)
865     return prefs[type];
866   return NULL;
867 }
868
869
870 /**
871  * Change preferences for the given peer. Preference changes are forgotten if peers
872  * disconnect.
873  *
874  * @param ph performance handle
875  * @param peer identifies the peer
876  * @param ... 0-terminated specification of the desired changes
877  */
878 void
879 GNUNET_ATS_performance_change_preference (struct GNUNET_ATS_PerformanceHandle *ph,
880                               const struct GNUNET_PeerIdentity *peer, ...)
881 {
882   struct PendingMessage *p;
883   struct ChangePreferenceMessage *m;
884   size_t msize;
885   uint32_t count;
886   struct PreferenceInformation *pi;
887   va_list ap;
888   enum GNUNET_ATS_PreferenceKind kind;
889
890   count = 0;
891   va_start (ap, peer);
892   while (GNUNET_ATS_PREFERENCE_END !=
893          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind)))
894   {
895     switch (kind)
896     {
897     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
898       count++;
899       (void) va_arg (ap, double);
900
901       break;
902     case GNUNET_ATS_PREFERENCE_LATENCY:
903       count++;
904       (void) va_arg (ap, double);
905
906       break;
907     default:
908       GNUNET_assert (0);
909     }
910   }
911   va_end (ap);
912   msize =
913       count * sizeof (struct PreferenceInformation) +
914       sizeof (struct ChangePreferenceMessage);
915   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
916   p->size = msize;
917   p->is_init = GNUNET_NO;
918   m = (struct ChangePreferenceMessage *) &p[1];
919   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE);
920   m->header.size = htons (msize);
921   m->num_preferences = htonl (count);
922   m->peer = *peer;
923   pi = (struct PreferenceInformation *) &m[1];
924   count = 0;
925   va_start (ap, peer);
926   while (GNUNET_ATS_PREFERENCE_END !=
927          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind)))
928   {
929     pi[count].preference_kind = htonl (kind);
930     switch (kind)
931     {
932     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
933       pi[count].preference_value = (float) va_arg (ap, double);
934
935       count++;
936       break;
937     case GNUNET_ATS_PREFERENCE_LATENCY:
938       pi[count].preference_value = (float) va_arg (ap, double);
939
940       count++;
941       break;
942     default:
943       GNUNET_assert (0);
944     }
945   }
946   va_end (ap);
947   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head, ph->pending_tail, p);
948   do_transmit (ph);
949 }
950
951 /**
952  * Send feedback to ATS on how good a the requirements for a peer and a
953  * preference is satisfied by ATS
954  *
955  * @param ph performance handle
956  * @param scope the time interval this valid for: [now - scope .. now]
957  * @param peer identifies the peer
958  * @param ... 0-terminated specification of the desired changes
959  */
960 void
961 GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph,
962                                                                                                                                                         const struct GNUNET_PeerIdentity *peer,
963                                                                                                                                                         const struct GNUNET_TIME_Relative scope, ...)
964 {
965   struct PendingMessage *p;
966   struct FeedbackPreferenceMessage *m;
967   size_t msize;
968   uint32_t count;
969   struct PreferenceInformation *pi;
970   va_list ap;
971   enum GNUNET_ATS_PreferenceKind kind;
972
973   count = 0;
974   va_start (ap, scope);
975   while (GNUNET_ATS_PREFERENCE_END !=
976          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind)))
977   {
978     switch (kind)
979     {
980     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
981       count++;
982       (void) va_arg (ap, double);
983
984       break;
985     case GNUNET_ATS_PREFERENCE_LATENCY:
986       count++;
987       (void) va_arg (ap, double);
988
989       break;
990     default:
991       GNUNET_assert (0);
992     }
993   }
994   va_end (ap);
995   msize =
996       count * sizeof (struct PreferenceInformation) +
997       sizeof (struct FeedbackPreferenceMessage);
998   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
999   p->size = msize;
1000   p->is_init = GNUNET_NO;
1001   m = (struct FeedbackPreferenceMessage *) &p[1];
1002   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK);
1003   m->header.size = htons (msize);
1004   m->scope = GNUNET_TIME_relative_hton (scope);
1005   m->num_feedback = htonl (count);
1006   m->peer = *peer;
1007   pi = (struct PreferenceInformation *) &m[1];
1008   count = 0;
1009   va_start (ap, scope);
1010   while (GNUNET_ATS_PREFERENCE_END !=
1011          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind)))
1012   {
1013     pi[count].preference_kind = htonl (kind);
1014     switch (kind)
1015     {
1016     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1017       pi[count].preference_value = (float) va_arg (ap, double);
1018
1019       count++;
1020       break;
1021     case GNUNET_ATS_PREFERENCE_LATENCY:
1022       pi[count].preference_value = (float) va_arg (ap, double);
1023
1024       count++;
1025       break;
1026     default:
1027       GNUNET_assert (0);
1028     }
1029   }
1030   va_end (ap);
1031   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head, ph->pending_tail, p);
1032   do_transmit (ph);
1033 }
1034
1035 /* end of ats_api_performance.c */