-more doxygen fixes
[oweals/gnunet.git] / src / ats / ats_api_scheduling.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_scheduling.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 #define DEBUG_ATS GNUNET_EXTRA_LOGGING
31
32 /**
33  * Message in linked list we should send to the ATS service.  The
34  * actual binary message follows this struct.
35  */
36 struct PendingMessage
37 {
38
39   /**
40    * Kept in a DLL.
41    */
42   struct PendingMessage *next;
43
44   /**
45    * Kept in a DLL.
46    */
47   struct PendingMessage *prev;
48
49   /**
50    * Size of the message.
51    */
52   size_t size;
53
54   /**
55    * Is this the 'ATS_START' message?
56    */
57   int is_init;
58 };
59
60
61 /**
62  * Information we track per session.
63  */
64 struct SessionRecord
65 {
66   /**
67    * Identity of the peer (just needed for error checking).
68    */
69   struct GNUNET_PeerIdentity peer;
70
71   /**
72    * Session handle.
73    */
74   struct Session *session;
75
76   /**
77    * Set to GNUNET_YES if the slot is used.
78    */
79   int slot_used;
80 };
81
82
83 /**
84  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
85  */
86 struct GNUNET_ATS_SchedulingHandle
87 {
88
89   /**
90    * Our configuration.
91    */
92   const struct GNUNET_CONFIGURATION_Handle *cfg;
93
94   /**
95    * Callback to invoke on suggestions.
96    */
97   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
98
99   /**
100    * Closure for 'suggest_cb'.
101    */
102   void *suggest_cb_cls;
103
104   /**
105    * Connection to ATS service.
106    */
107   struct GNUNET_CLIENT_Connection *client;
108
109   /**
110    * Head of list of messages for the ATS service.
111    */
112   struct PendingMessage *pending_head;
113
114   /**
115    * Tail of list of messages for the ATS service
116    */
117   struct PendingMessage *pending_tail;
118
119   /**
120    * Current request for transmission to ATS.
121    */
122   struct GNUNET_CLIENT_TransmitHandle *th;
123
124   /**
125    * Array of session objects (we need to translate them to numbers and back
126    * for the protocol; the offset in the array is the session number on the
127    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
128    * Unused entries are also NULL.
129    */
130   struct SessionRecord *session_array;
131
132   /**
133    * Task to trigger reconnect.
134    */
135   GNUNET_SCHEDULER_TaskIdentifier task;
136
137   /**
138    * Size of the session array.
139    */
140   unsigned int session_array_size;
141
142   /**
143    * Should we reconnect to ATS due to some serious error?
144    */
145   int reconnect;
146 };
147
148
149 /**
150  * Re-establish the connection to the ATS service.
151  *
152  * @param sh handle to use to re-connect.
153  */
154 static void
155 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
156
157
158 /**
159  * Re-establish the connection to the ATS service.
160  *
161  * @param cls handle to use to re-connect.
162  * @param tc scheduler context
163  */
164 static void
165 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
166 {
167   struct GNUNET_ATS_SchedulingHandle *sh = cls;
168
169   sh->task = GNUNET_SCHEDULER_NO_TASK;
170   reconnect (sh);
171 }
172
173
174 /**
175  * Disconnect from ATS and then reconnect.
176  *
177  * @param sh our handle
178  */
179 static void
180 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
181 {
182   sh->reconnect = GNUNET_NO;
183   GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
184   sh->client = NULL;
185   sh->task =
186       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
187                                     sh);
188 }
189
190
191 /**
192  * Transmit messages from the message queue to the service
193  * (if there are any, and if we are not already trying).
194  *
195  * @param sh handle to use
196  */
197 static void
198 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh);
199
200
201 /**
202  * Type of a function to call when we receive a message
203  * from the service.
204  *
205  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
206  * @param msg message received, NULL on timeout or fatal error
207  */
208 static void
209 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg);
210
211
212 /**
213  * We can now transmit a message to ATS. Do it.
214  *
215  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
216  * @param size number of bytes we can transmit to ATS
217  * @param buf where to copy the messages
218  * @return number of bytes copied into buf
219  */
220 static size_t
221 transmit_message_to_ats (void *cls, size_t size, void *buf)
222 {
223   struct GNUNET_ATS_SchedulingHandle *sh = cls;
224   struct PendingMessage *p;
225   size_t ret;
226   char *cbuf;
227
228   sh->th = NULL;
229   if ((size == 0) || (buf == NULL))
230   {
231     force_reconnect (sh);
232     return 0;
233   }
234   ret = 0;
235   cbuf = buf;
236   while ((NULL != (p = sh->pending_head)) && (p->size <= size))
237   {
238     memcpy (&cbuf[ret], &p[1], p->size);
239     ret += p->size;
240     size -= p->size;
241     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
242     if (GNUNET_YES == p->is_init)
243       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
244                              GNUNET_TIME_UNIT_FOREVER_REL);
245     GNUNET_free (p);
246   }
247   do_transmit (sh);
248   return ret;
249 }
250
251
252 /**
253  * Transmit messages from the message queue to the service
254  * (if there are any, and if we are not already trying).
255  *
256  * @param sh handle to use
257  */
258 static void
259 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh)
260 {
261   struct PendingMessage *p;
262
263   if (NULL != sh->th)
264     return;
265   if (NULL == (p = sh->pending_head))
266     return;
267   if (NULL == sh->client)
268     return;                     /* currently reconnecting */
269   sh->th =
270       GNUNET_CLIENT_notify_transmit_ready (sh->client, p->size,
271                                            GNUNET_TIME_UNIT_FOREVER_REL,
272                                            GNUNET_NO, &transmit_message_to_ats,
273                                            sh);
274 }
275
276
277 /**
278  * Find the session object corresponding to the given session ID.
279  *
280  * @param sh our handle
281  * @param session_id current session ID
282  * @param peer peer the session belongs to
283  * @return the session object (or NULL)
284  */
285 static struct Session *
286 find_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
287               const struct GNUNET_PeerIdentity *peer)
288 {
289 #if DEBUG_ATS
290   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291               "Find session %u from peer %s in %p\n",
292               (unsigned int) session_id,
293               GNUNET_i2s (peer),
294               sh);
295 #endif
296   if (session_id >= sh->session_array_size)
297   {
298     GNUNET_break (0);
299     return NULL;
300   }
301   if (0 == session_id)
302     return NULL;
303   if (sh->session_array[session_id].session == NULL)
304   {
305     GNUNET_break (0 ==
306                   memcmp (peer, &sh->session_array[session_id].peer,
307                           sizeof (struct GNUNET_PeerIdentity)));
308     return NULL;
309   }
310
311   if (0 !=
312       memcmp (peer, &sh->session_array[session_id].peer,
313              sizeof (struct GNUNET_PeerIdentity)))
314   {
315     GNUNET_break (0);
316     sh->reconnect = GNUNET_YES;
317     return NULL;
318   }
319   return sh->session_array[session_id].session;
320 }
321
322
323 /**
324  * Get the ID for the given session object.  If we do not have an ID for
325  * the given session object, allocate one.
326  *
327  * @param sh our handle
328  * @param session session object
329  * @param peer peer the session belongs to
330  * @return the session id
331  */
332 static uint32_t
333 get_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
334                 const struct GNUNET_PeerIdentity *peer)
335 {
336   unsigned int i;
337   unsigned int f;
338
339 #if DEBUG_ATS
340   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341               "Get session ID for session %p from peer %s in %p\n",
342               session,
343               GNUNET_i2s (peer),
344               sh);
345 #endif
346   if (NULL == session)
347     return 0;
348   f = 0;
349   for (i = 1; i < sh->session_array_size; i++)
350   {
351     if (session == sh->session_array[i].session)
352     {
353       GNUNET_assert (0 == memcmp (peer, &sh->session_array[i].peer,
354                                   sizeof (struct GNUNET_PeerIdentity)));
355       return i;
356     }
357     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
358       f = i;
359   }
360   if (f == 0)
361   {
362     f = sh->session_array_size;
363     GNUNET_array_grow (sh->session_array, sh->session_array_size,
364                        sh->session_array_size * 2);
365   }
366   GNUNET_assert (f > 0);
367   sh->session_array[f].session = session;
368   sh->session_array[f].peer = *peer;
369   sh->session_array[f].slot_used = GNUNET_YES;
370 #if DEBUG_ATS
371   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372               "Assigning session ID %u for session %p of peer %s in %p\n",
373               f,
374               session,
375               GNUNET_i2s (peer),
376               sh);
377 #endif
378   return f;
379 }
380
381
382 /**
383  * Remove the session of the given session ID from the session
384  * table (it is no longer valid).
385  *
386  * @param sh our handle
387  * @param session_id identifies session that is no longer valid
388  * @param peer peer the session belongs to
389  */
390 static void
391 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
392                 const struct GNUNET_PeerIdentity *peer)
393 {
394 #if DEBUG_ATS
395   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
396               "Remove sessionID %u from peer %s in %p\n",
397               (unsigned int) session_id,
398               GNUNET_i2s (peer),
399               sh);
400 #endif
401   if (0 == session_id)
402     return;
403   GNUNET_assert (session_id < sh->session_array_size);
404   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
405   GNUNET_assert (0 == memcmp (peer, &sh->session_array[session_id].peer,
406                          sizeof (struct GNUNET_PeerIdentity)));
407   sh->session_array[session_id].session = NULL;
408 }
409
410
411 /**
412  * Release the session slot from the session table (ATS service is
413  * also done using it).
414  *
415  * @param sh our handle
416  * @param session_id identifies session that is no longer valid
417  * @param peer peer the session belongs to
418  */
419 static void
420 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
421                  const struct GNUNET_PeerIdentity *peer)
422 {
423 #if DEBUG_ATS
424   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
425               "Release sessionID %u from peer %s in %p\n",
426               (unsigned int) session_id,
427               GNUNET_i2s (peer),
428               sh);
429 #endif
430   if (session_id >= sh->session_array_size)
431   {
432     GNUNET_break (0);
433     sh->reconnect = GNUNET_YES;
434     return;
435   }
436
437   /* this slot should have been removed from remove_session before */
438   GNUNET_assert (sh->session_array[session_id].session == NULL);
439
440   if (0 != memcmp (peer, &sh->session_array[session_id].peer,
441               sizeof (struct GNUNET_PeerIdentity)))
442   {
443     GNUNET_break (0);
444     sh->reconnect = GNUNET_YES;
445     return;
446   }
447   sh->session_array[session_id].slot_used = GNUNET_NO;
448   memset (&sh->session_array[session_id].peer, 0,
449           sizeof (struct GNUNET_PeerIdentity));
450 }
451
452
453 static void
454 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
455                          const struct SessionReleaseMessage *srm)
456 {
457   release_session (sh, ntohl (srm->session_id), &srm->peer);
458 }
459
460
461 /**
462  * Type of a function to call when we receive a message
463  * from the service.
464  *
465  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
466  * @param msg message received, NULL on timeout or fatal error
467  */
468 static void
469 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
470 {
471   struct GNUNET_ATS_SchedulingHandle *sh = cls;
472   const struct AddressSuggestionMessage *m;
473   const struct GNUNET_ATS_Information *atsi;
474   const char *plugin_address;
475   const char *plugin_name;
476   uint16_t plugin_address_length;
477   uint16_t plugin_name_length;
478   uint32_t ats_count;
479   struct GNUNET_HELLO_Address address;
480   struct Session * s;
481
482   if (NULL == msg)
483   {
484     force_reconnect (sh);
485     return;
486   }
487   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
488       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
489   {
490     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
491     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
492                            GNUNET_TIME_UNIT_FOREVER_REL);
493     if (GNUNET_YES == sh->reconnect)
494       force_reconnect (sh);
495     return;
496   }
497   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
498       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
499   {
500     GNUNET_break (0);
501     force_reconnect (sh);
502     return;
503   }
504   m = (const struct AddressSuggestionMessage *) msg;
505   ats_count = ntohl (m->ats_count);
506   plugin_address_length = ntohs (m->address_length);
507   atsi = (const struct GNUNET_ATS_Information *) &m[1];
508   plugin_address = (const char *) &atsi[ats_count];
509   plugin_name = &plugin_address[plugin_address_length];
510   plugin_name_length = ntohs (m->plugin_name_length);
511   if ((plugin_address_length + plugin_name_length +
512        ats_count * sizeof (struct GNUNET_ATS_Information) +
513        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
514       (ats_count >
515        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
516       || (plugin_name[plugin_name_length - 1] != '\0'))
517   {
518     GNUNET_break (0);
519     force_reconnect (sh);
520     return;
521   }
522   uint32_t session_id =  ntohl (m->session_id);
523
524   if (session_id == 0)
525     s = NULL;
526   else
527   {
528     s = find_session (sh, session_id, &m->peer);
529     if (s == NULL)
530     {
531 #if DEBUG_ATS
532       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
533                   "ATS tries to use outdated session `%s'\n", 
534                   GNUNET_i2s(&m->peer));
535 #endif
536       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
537                              GNUNET_TIME_UNIT_FOREVER_REL);
538       return;
539     }
540   }
541   address.peer = m->peer;
542   address.address = plugin_address;
543   address.address_length = plugin_address_length;
544   address.transport_name = plugin_name;
545
546   if ((s == NULL) && (0 == address.address_length))
547   {
548     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
549         "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
550         GNUNET_i2s(&address.peer) , address.transport_name, plugin_address_length, session_id);
551     GNUNET_break_op (0);
552     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
553                            GNUNET_TIME_UNIT_FOREVER_REL);
554     return;
555   }
556
557   sh->suggest_cb (sh->suggest_cb_cls, &address, s, m->bandwidth_out,
558                   m->bandwidth_in, atsi, ats_count);
559
560   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
561                          GNUNET_TIME_UNIT_FOREVER_REL);
562   if (GNUNET_YES == sh->reconnect)
563     force_reconnect (sh);
564 }
565
566
567 /**
568  * Re-establish the connection to the ATS service.
569  *
570  * @param sh handle to use to re-connect.
571  */
572 static void
573 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
574 {
575   struct PendingMessage *p;
576   struct ClientStartMessage *init;
577
578   GNUNET_assert (NULL == sh->client);
579   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
580   GNUNET_assert (NULL != sh->client);
581   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
582   {
583     p = GNUNET_malloc (sizeof (struct PendingMessage) +
584                        sizeof (struct ClientStartMessage));
585     p->size = sizeof (struct ClientStartMessage);
586     p->is_init = GNUNET_YES;
587     init = (struct ClientStartMessage *) &p[1];
588     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
589     init->header.size = htons (sizeof (struct ClientStartMessage));
590     init->start_flag = htonl (START_FLAG_SCHEDULING);
591     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
592   }
593   do_transmit (sh);
594 }
595
596
597 /**
598  * Initialize the ATS subsystem.
599  *
600  * @param cfg configuration to use
601  * @param suggest_cb notification to call whenever the suggestation changed
602  * @param suggest_cb_cls closure for 'suggest_cb'
603  * @return ats context
604  */
605 struct GNUNET_ATS_SchedulingHandle *
606 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
607                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
608                             void *suggest_cb_cls)
609 {
610   struct GNUNET_ATS_SchedulingHandle *sh;
611
612   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
613   sh->cfg = cfg;
614   sh->suggest_cb = suggest_cb;
615   sh->suggest_cb_cls = suggest_cb_cls;
616   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
617   reconnect (sh);
618   return sh;
619 }
620
621
622 /**
623  * Client is done with ATS scheduling, release resources.
624  *
625  * @param sh handle to release
626  */
627 void
628 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
629 {
630   struct PendingMessage *p;
631
632   while (NULL != (p = sh->pending_head))
633   {
634     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
635     GNUNET_free (p);
636   }
637   if (NULL != sh->client)
638   {
639     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
640     sh->client = NULL;
641   }
642   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
643   {
644     GNUNET_SCHEDULER_cancel (sh->task);
645     sh->task = GNUNET_SCHEDULER_NO_TASK;
646   }
647   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
648   GNUNET_free (sh);
649 }
650
651
652 /**
653  * We would like to establish a new connection with a peer.  ATS
654  * should suggest a good address to begin with.
655  *
656  * @param sh handle
657  * @param peer identity of the peer we need an address for
658  */
659 void
660 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
661                             const struct GNUNET_PeerIdentity *peer)
662 {
663   struct PendingMessage *p;
664   struct RequestAddressMessage *m;
665
666   p = GNUNET_malloc (sizeof (struct PendingMessage) +
667                      sizeof (struct RequestAddressMessage));
668   p->size = sizeof (struct RequestAddressMessage);
669   p->is_init = GNUNET_NO;
670   m = (struct RequestAddressMessage *) &p[1];
671   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
672   m->header.size = htons (sizeof (struct RequestAddressMessage));
673   m->reserved = htonl (0);
674   m->peer = *peer;
675   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
676   do_transmit (sh);
677 }
678
679
680 /**
681  * We would like to stop receiving address updates for this peer
682  *
683  * @param sh handle
684  * @param peer identity of the peer
685  */
686 void
687 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
688                                    const struct GNUNET_PeerIdentity *peer)
689 {
690   struct PendingMessage *p;
691   struct RequestAddressMessage *m;
692
693   p = GNUNET_malloc (sizeof (struct PendingMessage) +
694                      sizeof (struct RequestAddressMessage));
695   p->size = sizeof (struct RequestAddressMessage);
696   p->is_init = GNUNET_NO;
697   m = (struct RequestAddressMessage *) &p[1];
698   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
699   m->header.size = htons (sizeof (struct RequestAddressMessage));
700   m->reserved = htonl (0);
701   m->peer = *peer;
702   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
703   do_transmit (sh);
704 }
705
706 /**
707  * We have updated performance statistics for a given address.  Note
708  * that this function can be called for addresses that are currently
709  * in use as well as addresses that are valid but not actively in use.
710  * Furthermore, the peer may not even be connected to us right now (in
711  * which case the call may be ignored or the information may be stored
712  * for later use).  Update bandwidth assignments.
713  *
714  * @param sh handle
715  * @param address the address
716  * @param session session handle (if available)
717  * @param ats performance data for the address
718  * @param ats_count number of performance records in 'ats'
719  */
720 void
721 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
722                            const struct GNUNET_HELLO_Address *address,
723                            struct Session *session,
724                            const struct GNUNET_ATS_Information *ats,
725                            uint32_t ats_count)
726 {
727   struct PendingMessage *p;
728   struct AddressUpdateMessage *m;
729   struct GNUNET_ATS_Information *am;
730   char *pm;
731   size_t namelen;
732   size_t msize;
733
734   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
735   msize =
736       sizeof (struct AddressUpdateMessage) + address->address_length +
737       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
738   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
739       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
740       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
741       (ats_count >=
742        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
743   {
744     GNUNET_break (0);
745     return;
746   }
747
748   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
749   p->size = msize;
750   p->is_init = GNUNET_NO;
751   m = (struct AddressUpdateMessage *) &p[1];
752   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
753   m->header.size = htons (msize);
754   m->ats_count = htonl (ats_count);
755   m->peer = address->peer;
756   m->address_length = htons (address->address_length);
757   m->plugin_name_length = htons (namelen);
758   m->session_id = htonl (get_session_id (sh, session, &address->peer));
759   am = (struct GNUNET_ATS_Information *) &m[1];
760   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
761   pm = (char *) &am[ats_count];
762   memcpy (pm, address->address, address->address_length);
763   memcpy (&pm[address->address_length], address->transport_name, namelen);
764   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
765   do_transmit (sh);
766 }
767
768
769 /**
770  * An address is now in use or not used any more.
771  *
772  * @param sh handle
773  * @param address the address
774  * @param session session handle
775  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
776  * if address is not used any more
777  */
778 void
779 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
780                            const struct GNUNET_HELLO_Address *address,
781                            struct Session *session,
782                            int in_use)
783 {
784   struct PendingMessage *p;
785   struct AddressUseMessage *m;
786   char *pm;
787   size_t namelen;
788   size_t msize;
789
790   GNUNET_assert (NULL != address);
791   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
792   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
793   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
794       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
795       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
796   {
797     GNUNET_break (0);
798     return;
799   }
800
801   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
802   p->size = msize;
803   p->is_init = GNUNET_NO;
804   m = (struct AddressUseMessage *) &p[1];
805   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
806   m->header.size = htons (msize);
807   m->peer = address->peer;
808   m->in_use = htons (in_use);
809   m->address_length = htons (address->address_length);
810   m->plugin_name_length = htons (namelen);
811   m->session_id = htonl (get_session_id (sh, session, &address->peer));
812   pm = (char *) &m[1];
813   memcpy (pm, address->address, address->address_length);
814   memcpy (&pm[address->address_length], address->transport_name, namelen);
815   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
816
817   do_transmit (sh);
818 }
819
820 /**
821  * A session got destroyed, stop including it as a valid address.
822  *
823  * @param sh handle
824  * @param address the address
825  * @param session session handle that is no longer valid
826  */
827 void
828 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
829                               const struct GNUNET_HELLO_Address *address,
830                               struct Session *session)
831 {
832   struct PendingMessage *p;
833   struct AddressDestroyedMessage *m;
834   char *pm;
835   size_t namelen;
836   size_t msize;
837   uint32_t session_id;
838
839   GNUNET_assert (address->transport_name != NULL);
840   namelen = strlen (address->transport_name) + 1;
841   GNUNET_assert (namelen > 1);
842   msize = sizeof (struct AddressDestroyedMessage) + address->address_length + namelen;
843   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
844       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
845       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
846   {
847     GNUNET_break (0);
848     return;
849   }
850
851   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
852   p->size = msize;
853   p->is_init = GNUNET_NO;
854   m = (struct AddressDestroyedMessage *) &p[1];
855   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
856   m->header.size = htons (msize);
857   m->reserved = htonl (0);
858   m->peer = address->peer;
859   m->address_length = htons (address->address_length);
860   m->plugin_name_length = htons (namelen);
861   session_id = get_session_id (sh, session, &address->peer);
862   m->session_id = htonl (session_id);
863   pm = (char *) &m[1];
864   memcpy (pm, address->address, address->address_length);
865   memcpy (&pm[address->address_length], address->transport_name, namelen);
866   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
867   do_transmit (sh);
868   remove_session (sh, session_id, &address->peer);
869 }
870
871 /* end of ats_api_scheduling.c */