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