fixing reconnect bug
[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   sh->suggest_cb (sh->suggest_cb_cls, &address, s, m->bandwidth_out,
546                   m->bandwidth_in, atsi, ats_count);
547
548   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
549                          GNUNET_TIME_UNIT_FOREVER_REL);
550   if (GNUNET_YES == sh->reconnect)
551     force_reconnect (sh);
552 }
553
554
555 /**
556  * Re-establish the connection to the ATS service.
557  *
558  * @param sh handle to use to re-connect.
559  */
560 static void
561 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
562 {
563   struct PendingMessage *p;
564   struct ClientStartMessage *init;
565
566   GNUNET_assert (NULL == sh->client);
567   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
568   GNUNET_assert (NULL != sh->client);
569   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
570   {
571     p = GNUNET_malloc (sizeof (struct PendingMessage) +
572                        sizeof (struct ClientStartMessage));
573     p->size = sizeof (struct ClientStartMessage);
574     p->is_init = GNUNET_YES;
575     init = (struct ClientStartMessage *) &p[1];
576     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
577     init->header.size = htons (sizeof (struct ClientStartMessage));
578     init->start_flag = htonl (START_FLAG_SCHEDULING);
579     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
580   }
581   do_transmit (sh);
582 }
583
584
585 /**
586  * Initialize the ATS subsystem.
587  *
588  * @param cfg configuration to use
589  * @param suggest_cb notification to call whenever the suggestation changed
590  * @param suggest_cb_cls closure for 'suggest_cb'
591  * @return ats context
592  */
593 struct GNUNET_ATS_SchedulingHandle *
594 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
595                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
596                             void *suggest_cb_cls)
597 {
598   struct GNUNET_ATS_SchedulingHandle *sh;
599
600   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
601   sh->cfg = cfg;
602   sh->suggest_cb = suggest_cb;
603   sh->suggest_cb_cls = suggest_cb_cls;
604   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
605   reconnect (sh);
606   return sh;
607 }
608
609
610 /**
611  * Client is done with ATS scheduling, release resources.
612  *
613  * @param sh handle to release
614  */
615 void
616 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
617 {
618   struct PendingMessage *p;
619
620   while (NULL != (p = sh->pending_head))
621   {
622     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
623     GNUNET_free (p);
624   }
625   if (NULL != sh->client)
626   {
627     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
628     sh->client = NULL;
629   }
630   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
631   {
632     GNUNET_SCHEDULER_cancel (sh->task);
633     sh->task = GNUNET_SCHEDULER_NO_TASK;
634   }
635   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
636   GNUNET_free (sh);
637 }
638
639
640 /**
641  * We would like to establish a new connection with a peer.  ATS
642  * should suggest a good address to begin with.
643  *
644  * @param sh handle
645  * @param peer identity of the peer we need an address for
646  */
647 void
648 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
649                             const struct GNUNET_PeerIdentity *peer)
650 {
651   struct PendingMessage *p;
652   struct RequestAddressMessage *m;
653
654   p = GNUNET_malloc (sizeof (struct PendingMessage) +
655                      sizeof (struct RequestAddressMessage));
656   p->size = sizeof (struct RequestAddressMessage);
657   p->is_init = GNUNET_NO;
658   m = (struct RequestAddressMessage *) &p[1];
659   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
660   m->header.size = htons (sizeof (struct RequestAddressMessage));
661   m->reserved = htonl (0);
662   m->peer = *peer;
663   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
664   do_transmit (sh);
665 }
666
667
668 /**
669  * We would like to stop receiving address updates for this peer
670  *
671  * @param sh handle
672  * @param peer identity of the peer
673  */
674 void
675 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
676                                    const struct GNUNET_PeerIdentity *peer)
677 {
678   struct PendingMessage *p;
679   struct RequestAddressMessage *m;
680
681   p = GNUNET_malloc (sizeof (struct PendingMessage) +
682                      sizeof (struct RequestAddressMessage));
683   p->size = sizeof (struct RequestAddressMessage);
684   p->is_init = GNUNET_NO;
685   m = (struct RequestAddressMessage *) &p[1];
686   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
687   m->header.size = htons (sizeof (struct RequestAddressMessage));
688   m->reserved = htonl (0);
689   m->peer = *peer;
690   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
691   do_transmit (sh);
692 }
693
694 /**
695  * We have updated performance statistics for a given address.  Note
696  * that this function can be called for addresses that are currently
697  * in use as well as addresses that are valid but not actively in use.
698  * Furthermore, the peer may not even be connected to us right now (in
699  * which case the call may be ignored or the information may be stored
700  * for later use).  Update bandwidth assignments.
701  *
702  * @param sh handle
703  * @param address the address
704  * @param session session handle (if available)
705  * @param ats performance data for the address
706  * @param ats_count number of performance records in 'ats'
707  */
708 void
709 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
710                            const struct GNUNET_HELLO_Address *address,
711                            struct Session *session,
712                            const struct GNUNET_ATS_Information *ats,
713                            uint32_t ats_count)
714 {
715   struct PendingMessage *p;
716   struct AddressUpdateMessage *m;
717   struct GNUNET_ATS_Information *am;
718   char *pm;
719   size_t namelen;
720   size_t msize;
721
722   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
723   msize =
724       sizeof (struct AddressUpdateMessage) + address->address_length +
725       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
726   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
727       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
728       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
729       (ats_count >=
730        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
731   {
732     GNUNET_break (0);
733     return;
734   }
735
736   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
737   p->size = msize;
738   p->is_init = GNUNET_NO;
739   m = (struct AddressUpdateMessage *) &p[1];
740   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
741   m->header.size = htons (msize);
742   m->ats_count = htonl (ats_count);
743   m->peer = address->peer;
744   m->address_length = htons (address->address_length);
745   m->plugin_name_length = htons (namelen);
746   m->session_id = htonl (get_session_id (sh, session, &address->peer));
747   am = (struct GNUNET_ATS_Information *) &m[1];
748   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
749   pm = (char *) &am[ats_count];
750   memcpy (pm, address->address, address->address_length);
751   memcpy (&pm[address->address_length], address->transport_name, namelen);
752   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
753   do_transmit (sh);
754 }
755
756
757 /**
758  * An address is now in use or not used any more.
759  *
760  * @param sh handle
761  * @param address the address
762  * @param session session handle
763  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
764  * if address is not used any more
765  */
766 void
767 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
768                            const struct GNUNET_HELLO_Address *address,
769                            struct Session *session,
770                            int in_use)
771 {
772   struct PendingMessage *p;
773   struct AddressUseMessage *m;
774   char *pm;
775   size_t namelen;
776   size_t msize;
777
778   GNUNET_assert (NULL != address);
779   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
780   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
781   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
782       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
783       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
784   {
785     GNUNET_break (0);
786     return;
787   }
788
789   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
790   p->size = msize;
791   p->is_init = GNUNET_NO;
792   m = (struct AddressUseMessage *) &p[1];
793   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
794   m->header.size = htons (msize);
795   m->peer = address->peer;
796   m->in_use = htons (in_use);
797   m->address_length = htons (address->address_length);
798   m->plugin_name_length = htons (namelen);
799   m->session_id = htonl (get_session_id (sh, session, &address->peer));
800   pm = (char *) &m[1];
801   memcpy (pm, address->address, address->address_length);
802   memcpy (&pm[address->address_length], address->transport_name, namelen);
803   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
804
805   do_transmit (sh);
806 }
807
808 /**
809  * A session got destroyed, stop including it as a valid address.
810  *
811  * @param sh handle
812  * @param address the address
813  * @param session session handle that is no longer valid
814  */
815 void
816 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
817                               const struct GNUNET_HELLO_Address *address,
818                               struct Session *session)
819 {
820   struct PendingMessage *p;
821   struct AddressDestroyedMessage *m;
822   char *pm;
823   size_t namelen;
824   size_t msize;
825   uint32_t session_id;
826
827   GNUNET_assert (address->transport_name != NULL);
828   namelen = strlen (address->transport_name) + 1;
829   GNUNET_assert (namelen > 1);
830   msize = sizeof (struct AddressDestroyedMessage) + address->address_length + namelen;
831   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
832       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
833       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
834   {
835     GNUNET_break (0);
836     return;
837   }
838
839   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
840   p->size = msize;
841   p->is_init = GNUNET_NO;
842   m = (struct AddressDestroyedMessage *) &p[1];
843   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
844   m->header.size = htons (msize);
845   m->reserved = htonl (0);
846   m->peer = address->peer;
847   m->address_length = htons (address->address_length);
848   m->plugin_name_length = htons (namelen);
849   session_id = get_session_id (sh, session, &address->peer);
850   m->session_id = htonl (session_id);
851   pm = (char *) &m[1];
852   memcpy (pm, address->address, address->address_length);
853   memcpy (&pm[address->address_length], address->transport_name, namelen);
854   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
855   do_transmit (sh);
856   remove_session (sh, session_id, &address->peer);
857 }
858
859 /* end of ats_api_scheduling.c */