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