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