- refactor to check messages from both enc systems
[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    * 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  * @param tc scheduler context
265  */
266 static void
267 reconnect_task (void *cls,
268                 const struct GNUNET_SCHEDULER_TaskContext *tc)
269 {
270   struct GNUNET_ATS_PerformanceHandle *ph = cls;
271
272   ph->task = NULL;
273   reconnect (ph);
274 }
275
276
277 /**
278  * Transmit messages from the message queue to the service
279  * (if there are any, and if we are not already trying).
280  *
281  * @param ph handle to use
282  */
283 static void
284 do_transmit (struct GNUNET_ATS_PerformanceHandle *ph);
285
286
287 /**
288  * Type of a function to call when we receive a message
289  * from the service.
290  *
291  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
292  * @param msg message received, NULL on timeout or fatal error
293  */
294 static void
295 process_ats_message (void *cls,
296                      const struct GNUNET_MessageHeader *msg);
297
298
299 /**
300  * We can now transmit a message to ATS. Do it.
301  *
302  * @param cls the `struct GNUNET_ATS_PerformanceHandle`
303  * @param size number of bytes we can transmit to ATS
304  * @param buf where to copy the messages
305  * @return number of bytes copied into @a buf
306  */
307 static size_t
308 transmit_message_to_ats (void *cls,
309                          size_t size,
310                          void *buf)
311 {
312   struct GNUNET_ATS_PerformanceHandle *ph = cls;
313   struct PendingMessage *p;
314   size_t ret;
315   char *cbuf;
316
317   ph->th = NULL;
318   ret = 0;
319   cbuf = buf;
320   while ((NULL != (p = ph->pending_head)) && (p->size <= size))
321   {
322     memcpy (&cbuf[ret], &p[1], p->size);
323     ret += p->size;
324     size -= p->size;
325     GNUNET_CONTAINER_DLL_remove (ph->pending_head,
326                                  ph->pending_tail,
327                                  p);
328     GNUNET_free(p);
329   }
330   do_transmit (ph);
331   if (GNUNET_NO == ph->in_receive)
332   {
333     ph->in_receive = GNUNET_YES;
334     GNUNET_CLIENT_receive (ph->client,
335                            &process_ats_message,
336                            ph,
337                            GNUNET_TIME_UNIT_FOREVER_REL);
338   }
339   return ret;
340 }
341
342
343 /**
344  * Transmit messages from the message queue to the service
345  * (if there are any, and if we are not already trying).
346  *
347  * @param ph handle to use
348  */
349 static void
350 do_transmit (struct GNUNET_ATS_PerformanceHandle *ph)
351 {
352   struct PendingMessage *p;
353
354   if (NULL != ph->th)
355     return;
356   if (NULL == (p = ph->pending_head))
357     return;
358   if (NULL == ph->client)
359     return; /* currently reconnecting */
360   ph->th = GNUNET_CLIENT_notify_transmit_ready (ph->client,
361                                                 p->size,
362                                                 GNUNET_TIME_UNIT_FOREVER_REL,
363                                                 GNUNET_YES,
364                                                 &transmit_message_to_ats, ph);
365 }
366
367
368 /**
369  * We received a peer information message.  Validate and process it.
370  *
371  * @param ph our context with the callback
372  * @param msg the message
373  * @return #GNUNET_OK if the message was well-formed
374  */
375 static int
376 process_pi_message (struct GNUNET_ATS_PerformanceHandle *ph,
377                     const struct GNUNET_MessageHeader *msg)
378 {
379   const struct PeerInformationMessage *pi;
380   const char *plugin_address;
381   const char *plugin_name;
382   struct GNUNET_HELLO_Address address;
383   uint16_t plugin_address_length;
384   uint16_t plugin_name_length;
385   int addr_active;
386   struct GNUNET_ATS_Properties prop;
387
388   if (ntohs (msg->size) < sizeof(struct PeerInformationMessage))
389   {
390     GNUNET_break(0);
391     return GNUNET_SYSERR;
392   }
393   pi = (const struct PeerInformationMessage *) msg;
394   plugin_address_length = ntohs (pi->address_length);
395   plugin_name_length = ntohs (pi->plugin_name_length);
396   addr_active = (int) ntohl (pi->address_active);
397   plugin_address = (const char *) &pi[1];
398   plugin_name = &plugin_address[plugin_address_length];
399   if ((plugin_address_length + plugin_name_length
400       + sizeof(struct PeerInformationMessage) != ntohs (msg->size))
401       || (plugin_name[plugin_name_length - 1] != '\0'))
402   {
403     GNUNET_break(0);
404     return GNUNET_SYSERR;
405   }
406
407   if (NULL != ph->addr_info_cb)
408   {
409     GNUNET_ATS_properties_ntoh (&prop,
410                                 &pi->properties);
411     address.peer = pi->peer;
412     address.address = plugin_address;
413     address.address_length = plugin_address_length;
414     address.transport_name = plugin_name;
415     ph->addr_info_cb (ph->addr_info_cb_cls,
416                       &address,
417                       addr_active,
418                       pi->bandwidth_out,
419                       pi->bandwidth_in,
420                       &prop);
421   }
422   return GNUNET_OK;
423 }
424
425
426 /**
427  * We received a reservation result message.  Validate and process it.
428  *
429  * @param ph our context with the callback
430  * @param msg the message
431  * @return #GNUNET_OK if the message was well-formed
432  */
433 static int
434 process_rr_message (struct GNUNET_ATS_PerformanceHandle *ph,
435                     const struct GNUNET_MessageHeader *msg)
436 {
437   const struct ReservationResultMessage *rr;
438   struct GNUNET_ATS_ReservationContext *rc;
439   int32_t amount;
440
441   if (ntohs (msg->size) < sizeof(struct ReservationResultMessage))
442   {
443     GNUNET_break(0);
444     return GNUNET_SYSERR;
445   }
446   rr = (const struct ReservationResultMessage *) msg;
447   amount = ntohl (rr->amount);
448   rc = ph->reservation_head;
449   if (0 != memcmp (&rr->peer, &rc->peer, sizeof(struct GNUNET_PeerIdentity)))
450   {
451     GNUNET_break(0);
452     return GNUNET_SYSERR;
453   }
454   GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
455                                ph->reservation_tail,
456                                rc);
457   if ( (0 == amount) ||
458        (NULL != rc->rcb) )
459   {
460     /* tell client if not cancelled */
461     if (rc->rcb != NULL )
462       rc->rcb (rc->rcb_cls,
463                &rr->peer,
464                amount,
465                GNUNET_TIME_relative_ntoh (rr->res_delay));
466     GNUNET_free(rc);
467     return GNUNET_OK;
468   }
469   /* amount non-zero, but client cancelled, consider undo! */
470   if (GNUNET_YES != rc->undo)
471   {
472     GNUNET_free(rc);
473     return GNUNET_OK; /* do not try to undo failed undos or negative amounts */
474   }
475   GNUNET_free(rc);
476   (void) GNUNET_ATS_reserve_bandwidth (ph,
477                                        &rr->peer,
478                                        -amount,
479                                        NULL, NULL);
480   return GNUNET_OK;
481 }
482
483
484 /**
485  * We received a PeerInformationMessage.  Validate and process it.
486  *
487  * @param ph our context with the callback
488  * @param msg the message
489  * @return #GNUNET_OK if the message was well-formed
490  */
491 static int
492 process_ar_message (struct GNUNET_ATS_PerformanceHandle *ph,
493                     const struct GNUNET_MessageHeader *msg)
494 {
495   const struct PeerInformationMessage *pi;
496   struct GNUNET_ATS_AddressListHandle *alh;
497   struct GNUNET_ATS_AddressListHandle *next;
498   const char *plugin_address;
499   const char *plugin_name;
500   struct GNUNET_HELLO_Address address;
501   struct GNUNET_PeerIdentity allzeros;
502   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
503   struct GNUNET_ATS_Properties prop;
504   uint16_t plugin_address_length;
505   uint16_t plugin_name_length;
506   uint32_t active;
507   uint32_t id;
508
509   if (ntohs (msg->size) < sizeof(struct PeerInformationMessage))
510   {
511     GNUNET_break(0);
512     return GNUNET_SYSERR;
513   }
514   pi = (const struct PeerInformationMessage *) msg;
515   id = ntohl (pi->id);
516   active = ntohl (pi->address_active);
517   plugin_address_length = ntohs (pi->address_length);
518   plugin_name_length = ntohs (pi->plugin_name_length);
519   plugin_address = (const char *) &pi[1];
520   plugin_name = &plugin_address[plugin_address_length];
521   if ( (plugin_address_length + plugin_name_length
522         + sizeof (struct PeerInformationMessage) != ntohs (msg->size)) ||
523        (plugin_name[plugin_name_length - 1] != '\0') )
524   {
525     GNUNET_break(0);
526     return GNUNET_SYSERR;
527   }
528   LOG (GNUNET_ERROR_TYPE_DEBUG,
529        "Received ATS_ADDRESSLIST_RESPONSE message for peer %s and plugin %s\n",
530        GNUNET_i2s (&pi->peer),
531        plugin_name);
532
533   next = ph->addresslist_head;
534   while (NULL != (alh = next))
535   {
536     next = alh->next;
537     if (alh->id == id)
538       break;
539   }
540   if (NULL == alh)
541   {
542     /* was canceled */
543     return GNUNET_SYSERR;
544   }
545
546   memset (&allzeros, '\0', sizeof (allzeros));
547   if ( (0 == memcmp (&allzeros, &pi->peer, sizeof(allzeros))) &&
548        (0 == plugin_name_length) &&
549        (0 == plugin_address_length) )
550   {
551     /* Done */
552     LOG (GNUNET_ERROR_TYPE_DEBUG,
553          "Received last message for ATS_ADDRESSLIST_RESPONSE\n");
554     bandwidth_zero.value__ = htonl (0);
555     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
556                                  ph->addresslist_tail,
557                                  alh);
558     if (NULL != alh->cb)
559       alh->cb (alh->cb_cls,
560                NULL,
561                GNUNET_NO,
562                bandwidth_zero,
563                bandwidth_zero,
564                NULL);
565     GNUNET_free (alh);
566     return GNUNET_OK;
567   }
568
569   address.peer = pi->peer;
570   address.address = plugin_address;
571   address.address_length = plugin_address_length;
572   address.transport_name = plugin_name;
573   if ( ( (GNUNET_YES == alh->all_addresses) ||
574          (GNUNET_YES == active) ) &&
575        (NULL != alh->cb) )
576   {
577     GNUNET_ATS_properties_ntoh (&prop,
578                                 &pi->properties);
579     alh->cb (alh->cb_cls,
580              &address,
581              active,
582              pi->bandwidth_out,
583              pi->bandwidth_in,
584              &prop);
585   }
586   return GNUNET_OK;
587 }
588
589
590 /**
591  * Type of a function to call when we receive a message
592  * from the service.
593  *
594  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
595  * @param msg message received, NULL on timeout or fatal error
596  */
597 static void
598 process_ats_message (void *cls,
599                      const struct GNUNET_MessageHeader *msg)
600 {
601   struct GNUNET_ATS_PerformanceHandle *ph = cls;
602
603   if (NULL == msg)
604     goto reconnect;
605   switch (ntohs (msg->type))
606   {
607   case GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION:
608     if (GNUNET_OK != process_pi_message (ph, msg))
609     {
610       GNUNET_break (0);
611       goto reconnect;
612     }
613     break;
614   case GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT:
615     if (GNUNET_OK != process_rr_message (ph, msg))
616     {
617       GNUNET_break (0);
618       goto reconnect;
619     }
620     break;
621   case GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE:
622     if (GNUNET_OK != process_ar_message (ph, msg))
623     {
624       GNUNET_break (0);
625       goto reconnect;
626     }
627     break;
628   default:
629     GNUNET_break (0);
630     goto reconnect;
631   }
632   ph->backoff = GNUNET_TIME_UNIT_ZERO;
633   GNUNET_CLIENT_receive (ph->client,
634                          &process_ats_message,
635                          ph,
636                          GNUNET_TIME_UNIT_FOREVER_REL);
637   return;
638
639  reconnect:
640   LOG (GNUNET_ERROR_TYPE_DEBUG,
641        "Reconnecting!\n");
642   if (NULL != ph->th)
643   {
644     GNUNET_CLIENT_notify_transmit_ready_cancel (ph->th);
645     ph->th = NULL;
646   }
647   if (NULL != ph->client)
648   {
649     GNUNET_CLIENT_disconnect (ph->client);
650     ph->client = NULL;
651     ph->in_receive = GNUNET_NO;
652     if (NULL != ph->addr_info_cb)
653     {
654       /* Indicate reconnect */
655       ph->addr_info_cb (ph->addr_info_cb_cls,
656                         NULL,
657                         GNUNET_NO,
658                         GNUNET_BANDWIDTH_value_init (0),
659                         GNUNET_BANDWIDTH_value_init (0),
660                         NULL);
661     }
662   }
663   ph->backoff = GNUNET_TIME_STD_BACKOFF (ph->backoff);
664   ph->task = GNUNET_SCHEDULER_add_delayed (ph->backoff,
665                                            &reconnect_task,
666                                            ph);
667 }
668
669
670 /**
671  * Re-establish the connection to the ATS service.
672  *
673  * @param ph handle to use to re-connect.
674  */
675 static void
676 reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
677 {
678   struct PendingMessage *p;
679   struct ClientStartMessage *init;
680
681   GNUNET_assert (NULL == ph->client);
682   ph->client = GNUNET_CLIENT_connect ("ats",
683                                       ph->cfg);
684   GNUNET_assert (NULL != ph->client);
685   if ((NULL == (p = ph->pending_head)) || (GNUNET_YES != p->is_init))
686   {
687     p = GNUNET_malloc (sizeof (struct PendingMessage) +
688         sizeof (struct ClientStartMessage));
689     p->size = sizeof(struct ClientStartMessage);
690     p->is_init = GNUNET_YES;
691     init = (struct ClientStartMessage *) &p[1];
692     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
693     init->header.size = htons (sizeof(struct ClientStartMessage));
694     init->start_flag = htonl ( (NULL == ph->addr_info_cb)
695                                ? START_FLAG_PERFORMANCE_NO_PIC
696                                : START_FLAG_PERFORMANCE_WITH_PIC);
697     GNUNET_CONTAINER_DLL_insert (ph->pending_head,
698                                  ph->pending_tail,
699                                  p);
700   }
701   do_transmit (ph);
702 }
703
704
705 /**
706  * Get handle to access performance API of the ATS subsystem.
707  *
708  * @param cfg configuration to use
709  * @param addr_info_cb callback called when performance characteristics for
710  *      an address change
711  * @param addr_info_cb_cls closure for @a addr_info_cb
712  * @return ats performance context
713  */
714 struct GNUNET_ATS_PerformanceHandle *
715 GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
716                              GNUNET_ATS_AddressInformationCallback addr_info_cb,
717                              void *addr_info_cb_cls)
718 {
719   struct GNUNET_ATS_PerformanceHandle *ph;
720
721   ph = GNUNET_new (struct GNUNET_ATS_PerformanceHandle);
722   ph->cfg = cfg;
723   ph->addr_info_cb = addr_info_cb;
724   ph->addr_info_cb_cls = addr_info_cb_cls;
725   ph->id = 0;
726   reconnect (ph);
727   return ph;
728 }
729
730
731 /**
732  * Client is done using the ATS performance subsystem, release resources.
733  *
734  * @param ph handle
735  */
736 void
737 GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph)
738 {
739   struct PendingMessage *p;
740   struct GNUNET_ATS_ReservationContext *rc;
741   struct GNUNET_ATS_AddressListHandle *alh;
742
743   while (NULL != (p = ph->pending_head))
744   {
745     GNUNET_CONTAINER_DLL_remove (ph->pending_head,
746                                  ph->pending_tail,
747                                  p);
748     GNUNET_free (p);
749   }
750   while (NULL != (alh = ph->addresslist_head))
751   {
752     GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
753                                  ph->addresslist_tail,
754                                  alh);
755     GNUNET_free (alh);
756   }
757   while (NULL != (rc = ph->reservation_head))
758   {
759     GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
760                                  ph->reservation_tail,
761                                  rc);
762     GNUNET_break (NULL == rc->rcb);
763     GNUNET_free (rc);
764   }
765
766   if (NULL != ph->task)
767   {
768     GNUNET_SCHEDULER_cancel (ph->task);
769     ph->task = NULL;
770   }
771   if (NULL != ph->client)
772   {
773     GNUNET_CLIENT_disconnect (ph->client);
774     ph->client = NULL;
775   }
776   GNUNET_free (ph);
777 }
778
779
780 /**
781  * Reserve inbound bandwidth from the given peer.  ATS will look at
782  * the current amount of traffic we receive from the peer and ensure
783  * that the peer could add 'amount' of data to its stream.
784  *
785  * @param ph performance handle
786  * @param peer identifies the peer
787  * @param amount reserve N bytes for receiving, negative
788  *                amounts can be used to undo a (recent) reservation;
789  * @param rcb function to call with the resulting reservation information
790  * @param rcb_cls closure for @a rcb
791  * @return NULL on error
792  * @deprecated will be replaced soon
793  */
794 struct GNUNET_ATS_ReservationContext *
795 GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph,
796                               const struct GNUNET_PeerIdentity *peer,
797                               int32_t amount,
798                               GNUNET_ATS_ReservationCallback rcb, void *rcb_cls)
799 {
800   struct GNUNET_ATS_ReservationContext *rc;
801   struct PendingMessage *p;
802   struct ReservationRequestMessage *m;
803
804   rc = GNUNET_new (struct GNUNET_ATS_ReservationContext);
805   rc->size = amount;
806   rc->peer = *peer;
807   rc->rcb = rcb;
808   rc->rcb_cls = rcb_cls;
809   if ( (NULL != rcb) &&
810        (amount > 0) )
811     rc->undo = GNUNET_YES;
812   GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head,
813                                     ph->reservation_tail,
814                                     rc);
815
816   p = GNUNET_malloc (sizeof (struct PendingMessage) +
817       sizeof (struct ReservationRequestMessage));
818   p->size = sizeof(struct ReservationRequestMessage);
819   p->is_init = GNUNET_NO;
820   m = (struct ReservationRequestMessage *) &p[1];
821   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESERVATION_REQUEST);
822   m->header.size = htons (sizeof(struct ReservationRequestMessage));
823   m->amount = htonl (amount);
824   m->peer = *peer;
825   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head,
826                                     ph->pending_tail,
827                                     p);
828   do_transmit (ph);
829   return rc;
830 }
831
832
833 /**
834  * Cancel request for reserving bandwidth.
835  *
836  * @param rc context returned by the original GNUNET_ATS_reserve_bandwidth call
837  */
838 void
839 GNUNET_ATS_reserve_bandwidth_cancel (struct GNUNET_ATS_ReservationContext *rc)
840 {
841   rc->rcb = NULL;
842 }
843
844
845 /**
846  * Get information about addresses known to the ATS subsystem.
847  *
848  * @param handle the performance handle to use
849  * @param peer peer idm can be NULL for all peers
850  * @param all #GNUNET_YES to get information about all addresses or #GNUNET_NO to
851  *        get only address currently used
852  * @param infocb callback to call with the addresses,
853  *        will callback with address == NULL when done
854  * @param infocb_cls closure for @a infocb
855  * @return ats performance context
856  */
857 struct GNUNET_ATS_AddressListHandle*
858 GNUNET_ATS_performance_list_addresses (struct GNUNET_ATS_PerformanceHandle *handle,
859                                        const struct GNUNET_PeerIdentity *peer,
860                                        int all,
861                                        GNUNET_ATS_AddressInformationCallback infocb,
862                                        void *infocb_cls)
863 {
864   struct GNUNET_ATS_AddressListHandle *alh;
865   struct PendingMessage *p;
866   struct AddressListRequestMessage *m;
867
868   if (NULL == infocb)
869     return NULL;
870   alh = GNUNET_new (struct GNUNET_ATS_AddressListHandle);
871   alh->id = handle->id;
872   handle->id++;
873   alh->cb = infocb;
874   alh->cb_cls = infocb_cls;
875   alh->ph = handle;
876   alh->all_addresses = all;
877   if (NULL == peer)
878   {
879     alh->all_peers = GNUNET_YES;
880   }
881   else
882   {
883     alh->all_peers = GNUNET_NO;
884     alh->peer = *peer;
885   }
886   GNUNET_CONTAINER_DLL_insert (handle->addresslist_head,
887                                handle->addresslist_tail,
888                                alh);
889
890   p = GNUNET_malloc (sizeof (struct PendingMessage) +
891                      sizeof (struct AddressListRequestMessage));
892   p->size = sizeof (struct AddressListRequestMessage);
893   m = (struct AddressListRequestMessage *) &p[1];
894   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST);
895   m->header.size = htons (sizeof(struct AddressListRequestMessage));
896   m->all = htonl (all);
897   m->id = htonl (alh->id);
898   if (NULL != peer)
899     m->peer = *peer;
900   GNUNET_CONTAINER_DLL_insert_tail (handle->pending_head,
901                                     handle->pending_tail,
902                                     p);
903   do_transmit (handle);
904
905   return alh;
906 }
907
908
909 /**
910  * Cancel a pending address listing operation
911  *
912  * @param handle the handle of the request to cancel
913  */
914 void
915 GNUNET_ATS_performance_list_addresses_cancel (struct GNUNET_ATS_AddressListHandle *handle)
916 {
917   GNUNET_CONTAINER_DLL_remove (handle->ph->addresslist_head,
918                                handle->ph->addresslist_tail,
919                                handle);
920   GNUNET_free (handle);
921 }
922
923
924 /**
925  * Convert a `enum GNUNET_ATS_PreferenceType` to a string
926  *
927  * @param type the preference type
928  * @return a string or NULL if invalid
929  */
930 const char *
931 GNUNET_ATS_print_preference_type (uint32_t type)
932 {
933   const char *prefs[] = GNUNET_ATS_PreferenceTypeString;
934
935   if (type < GNUNET_ATS_PREFERENCE_END)
936     return prefs[type];
937   return NULL;
938 }
939
940
941 /**
942  * Change preferences for the given peer. Preference changes are forgotten if peers
943  * disconnect.
944  *
945  * @param ph performance handle
946  * @param peer identifies the peer
947  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
948  */
949 void
950 GNUNET_ATS_performance_change_preference (struct GNUNET_ATS_PerformanceHandle *ph,
951                                           const struct GNUNET_PeerIdentity *peer, ...)
952 {
953   struct PendingMessage *p;
954   struct ChangePreferenceMessage *m;
955   size_t msize;
956   uint32_t count;
957   struct PreferenceInformation *pi;
958   va_list ap;
959   enum GNUNET_ATS_PreferenceKind kind;
960
961   count = 0;
962   va_start(ap, peer);
963   while (GNUNET_ATS_PREFERENCE_END !=
964          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
965   {
966     switch (kind)
967     {
968     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
969       count++;
970       (void) va_arg (ap, double);
971       break;
972     case GNUNET_ATS_PREFERENCE_LATENCY:
973       count++;
974       (void) va_arg (ap, double);
975       break;
976     default:
977       GNUNET_assert(0);
978     }
979   }
980   va_end(ap);
981   msize = count * sizeof(struct PreferenceInformation)
982       + sizeof(struct ChangePreferenceMessage);
983   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
984   p->size = msize;
985   p->is_init = GNUNET_NO;
986   m = (struct ChangePreferenceMessage *) &p[1];
987   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE);
988   m->header.size = htons (msize);
989   m->num_preferences = htonl (count);
990   m->peer = *peer;
991   pi = (struct PreferenceInformation *) &m[1];
992   count = 0;
993   va_start(ap, peer);
994   while (GNUNET_ATS_PREFERENCE_END != (kind =
995       va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
996   {
997     pi[count].preference_kind = htonl (kind);
998     switch (kind)
999     {
1000     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1001       pi[count].preference_value = (float) va_arg (ap, double);
1002
1003       count++;
1004       break;
1005     case GNUNET_ATS_PREFERENCE_LATENCY:
1006       pi[count].preference_value = (float) va_arg (ap, double);
1007
1008       count++;
1009       break;
1010     default:
1011       GNUNET_assert(0);
1012     }
1013   }
1014   va_end(ap);
1015   GNUNET_CONTAINER_DLL_insert_tail(ph->pending_head, ph->pending_tail, p);
1016   do_transmit (ph);
1017 }
1018
1019
1020 /**
1021  * Send feedback to ATS on how good a the requirements for a peer and a
1022  * preference is satisfied by ATS
1023  *
1024  * @param ph performance handle
1025  * @param scope the time interval this valid for: [now - scope .. now]
1026  * @param peer identifies the peer
1027  * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
1028  */
1029 void
1030 GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph,
1031                                       const struct GNUNET_PeerIdentity *peer,
1032                                       const struct GNUNET_TIME_Relative scope, ...)
1033 {
1034   struct PendingMessage *p;
1035   struct FeedbackPreferenceMessage *m;
1036   size_t msize;
1037   uint32_t count;
1038   struct PreferenceInformation *pi;
1039   va_list ap;
1040   enum GNUNET_ATS_PreferenceKind kind;
1041
1042   count = 0;
1043   va_start(ap, scope);
1044   while (GNUNET_ATS_PREFERENCE_END !=
1045          (kind = va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
1046   {
1047     switch (kind)
1048     {
1049     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1050       count++;
1051       (void) va_arg (ap, double);
1052       break;
1053     case GNUNET_ATS_PREFERENCE_LATENCY:
1054       count++;
1055       (void) va_arg (ap, double);
1056       break;
1057     default:
1058       GNUNET_assert(0);
1059     }
1060   }
1061   va_end(ap);
1062   msize = count * sizeof(struct PreferenceInformation)
1063       + sizeof(struct FeedbackPreferenceMessage);
1064   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1065   p->size = msize;
1066   p->is_init = GNUNET_NO;
1067   m = (struct FeedbackPreferenceMessage *) &p[1];
1068   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK);
1069   m->header.size = htons (msize);
1070   m->scope = GNUNET_TIME_relative_hton (scope);
1071   m->num_feedback = htonl (count);
1072   m->peer = *peer;
1073   pi = (struct PreferenceInformation *) &m[1];
1074   count = 0;
1075   va_start(ap, scope);
1076   while (GNUNET_ATS_PREFERENCE_END != (kind =
1077       va_arg (ap, enum GNUNET_ATS_PreferenceKind) ))
1078   {
1079     pi[count].preference_kind = htonl (kind);
1080     switch (kind)
1081     {
1082     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1083       pi[count].preference_value = (float) va_arg (ap, double);
1084
1085       count++;
1086       break;
1087     case GNUNET_ATS_PREFERENCE_LATENCY:
1088       pi[count].preference_value = (float) va_arg (ap, double);
1089
1090       count++;
1091       break;
1092     default:
1093       GNUNET_assert(0);
1094     }
1095   }
1096   va_end(ap);
1097   GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head,
1098                                     ph->pending_tail,
1099                                     p);
1100   do_transmit (ph);
1101 }
1102
1103 /* end of ats_api_performance.c */