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