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