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