WAN/LAN detection
[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, "Find session %u from peer %s in %p\n",
291               (unsigned int) session_id, GNUNET_i2s (peer), sh);
292 #endif
293   if (session_id >= sh->session_array_size)
294   {
295     GNUNET_break (0);
296     return NULL;
297   }
298   if (0 == session_id)
299     return NULL;
300   if (sh->session_array[session_id].session == NULL)
301   {
302     GNUNET_break (0 ==
303                   memcmp (peer, &sh->session_array[session_id].peer,
304                           sizeof (struct GNUNET_PeerIdentity)));
305     return NULL;
306   }
307
308   if (0 !=
309       memcmp (peer, &sh->session_array[session_id].peer,
310               sizeof (struct GNUNET_PeerIdentity)))
311   {
312     GNUNET_break (0);
313     sh->reconnect = GNUNET_YES;
314     return NULL;
315   }
316   return sh->session_array[session_id].session;
317 }
318
319
320 /**
321  * Get the ID for the given session object.  If we do not have an ID for
322  * the given session object, allocate one.
323  *
324  * @param sh our handle
325  * @param session session object
326  * @param peer peer the session belongs to
327  * @return the session id
328  */
329 static uint32_t
330 get_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
331                 const struct GNUNET_PeerIdentity *peer)
332 {
333   unsigned int i;
334   unsigned int f;
335
336 #if DEBUG_ATS
337   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338               "Get session ID for session %p from peer %s in %p\n", session,
339               GNUNET_i2s (peer), sh);
340 #endif
341   if (NULL == session)
342     return 0;
343   f = 0;
344   for (i = 1; i < sh->session_array_size; i++)
345   {
346     if (session == sh->session_array[i].session)
347     {
348       GNUNET_assert (0 ==
349                      memcmp (peer, &sh->session_array[i].peer,
350                              sizeof (struct GNUNET_PeerIdentity)));
351       return i;
352     }
353     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
354       f = i;
355   }
356   if (f == 0)
357   {
358     f = sh->session_array_size;
359     GNUNET_array_grow (sh->session_array, sh->session_array_size,
360                        sh->session_array_size * 2);
361   }
362   GNUNET_assert (f > 0);
363   sh->session_array[f].session = session;
364   sh->session_array[f].peer = *peer;
365   sh->session_array[f].slot_used = GNUNET_YES;
366 #if DEBUG_ATS
367   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368               "Assigning session ID %u for session %p of peer %s in %p\n", f,
369               session, GNUNET_i2s (peer), sh);
370 #endif
371   return f;
372 }
373
374
375 /**
376  * Remove the session of the given session ID from the session
377  * table (it is no longer valid).
378  *
379  * @param sh our handle
380  * @param session_id identifies session that is no longer valid
381  * @param peer peer the session belongs to
382  */
383 static void
384 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
385                 const struct GNUNET_PeerIdentity *peer)
386 {
387 #if DEBUG_ATS
388   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
389               "Remove sessionID %u from peer %s in %p\n",
390               (unsigned int) session_id, GNUNET_i2s (peer), sh);
391 #endif
392   if (0 == session_id)
393     return;
394   GNUNET_assert (session_id < sh->session_array_size);
395   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
396   GNUNET_assert (0 ==
397                  memcmp (peer, &sh->session_array[session_id].peer,
398                          sizeof (struct GNUNET_PeerIdentity)));
399   sh->session_array[session_id].session = NULL;
400 }
401
402
403 /**
404  * Release the session slot from the session table (ATS service is
405  * also done using it).
406  *
407  * @param sh our handle
408  * @param session_id identifies session that is no longer valid
409  * @param peer peer the session belongs to
410  */
411 static void
412 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
413                  const struct GNUNET_PeerIdentity *peer)
414 {
415 #if DEBUG_ATS
416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417               "Release sessionID %u from peer %s in %p\n",
418               (unsigned int) session_id, GNUNET_i2s (peer), sh);
419 #endif
420   if (session_id >= sh->session_array_size)
421   {
422     GNUNET_break (0);
423     sh->reconnect = GNUNET_YES;
424     return;
425   }
426
427   /* this slot should have been removed from remove_session before */
428   GNUNET_assert (sh->session_array[session_id].session == NULL);
429
430   if (0 !=
431       memcmp (peer, &sh->session_array[session_id].peer,
432               sizeof (struct GNUNET_PeerIdentity)))
433   {
434     GNUNET_break (0);
435     sh->reconnect = GNUNET_YES;
436     return;
437   }
438   sh->session_array[session_id].slot_used = GNUNET_NO;
439   memset (&sh->session_array[session_id].peer, 0,
440           sizeof (struct GNUNET_PeerIdentity));
441 }
442
443
444 static void
445 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
446                          const struct SessionReleaseMessage *srm)
447 {
448   release_session (sh, ntohl (srm->session_id), &srm->peer);
449 }
450
451
452 /**
453  * Type of a function to call when we receive a message
454  * from the service.
455  *
456  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
457  * @param msg message received, NULL on timeout or fatal error
458  */
459 static void
460 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
461 {
462   struct GNUNET_ATS_SchedulingHandle *sh = cls;
463   const struct AddressSuggestionMessage *m;
464   const struct GNUNET_ATS_Information *atsi;
465   const char *plugin_address;
466   const char *plugin_name;
467   uint16_t plugin_address_length;
468   uint16_t plugin_name_length;
469   uint32_t ats_count;
470   struct GNUNET_HELLO_Address address;
471   struct Session *s;
472
473   if (NULL == msg)
474   {
475     force_reconnect (sh);
476     return;
477   }
478   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
479       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
480   {
481     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
482     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
483                            GNUNET_TIME_UNIT_FOREVER_REL);
484     if (GNUNET_YES == sh->reconnect)
485       force_reconnect (sh);
486     return;
487   }
488   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
489       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
490   {
491     GNUNET_break (0);
492     force_reconnect (sh);
493     return;
494   }
495   m = (const struct AddressSuggestionMessage *) msg;
496   ats_count = ntohl (m->ats_count);
497   plugin_address_length = ntohs (m->address_length);
498   atsi = (const struct GNUNET_ATS_Information *) &m[1];
499   plugin_address = (const char *) &atsi[ats_count];
500   plugin_name = &plugin_address[plugin_address_length];
501   plugin_name_length = ntohs (m->plugin_name_length);
502   if ((plugin_address_length + plugin_name_length +
503        ats_count * sizeof (struct GNUNET_ATS_Information) +
504        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
505       (ats_count >
506        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
507       || (plugin_name[plugin_name_length - 1] != '\0'))
508   {
509     GNUNET_break (0);
510     force_reconnect (sh);
511     return;
512   }
513   uint32_t session_id = ntohl (m->session_id);
514
515   if (session_id == 0)
516     s = NULL;
517   else
518   {
519     s = find_session (sh, session_id, &m->peer);
520     if (s == NULL)
521     {
522 #if DEBUG_ATS
523       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524                   "ATS tries to use outdated session `%s'\n",
525                   GNUNET_i2s (&m->peer));
526 #endif
527       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
528                              GNUNET_TIME_UNIT_FOREVER_REL);
529       return;
530     }
531   }
532   address.peer = m->peer;
533   address.address = plugin_address;
534   address.address_length = plugin_address_length;
535   address.transport_name = plugin_name;
536
537   if ((s == NULL) && (0 == address.address_length))
538   {
539     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
540                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
541                 GNUNET_i2s (&address.peer), address.transport_name,
542                 plugin_address_length, session_id);
543     GNUNET_break_op (0);
544     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
545                            GNUNET_TIME_UNIT_FOREVER_REL);
546     return;
547   }
548
549   sh->suggest_cb (sh->suggest_cb_cls, &address, s, m->bandwidth_out,
550                   m->bandwidth_in, atsi, ats_count);
551
552   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
553                          GNUNET_TIME_UNIT_FOREVER_REL);
554   if (GNUNET_YES == sh->reconnect)
555     force_reconnect (sh);
556 }
557
558
559 /**
560  * Re-establish the connection to the ATS service.
561  *
562  * @param sh handle to use to re-connect.
563  */
564 static void
565 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
566 {
567   struct PendingMessage *p;
568   struct ClientStartMessage *init;
569
570   GNUNET_assert (NULL == sh->client);
571   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
572   GNUNET_assert (NULL != sh->client);
573   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
574   {
575     p = GNUNET_malloc (sizeof (struct PendingMessage) +
576                        sizeof (struct ClientStartMessage));
577     p->size = sizeof (struct ClientStartMessage);
578     p->is_init = GNUNET_YES;
579     init = (struct ClientStartMessage *) &p[1];
580     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
581     init->header.size = htons (sizeof (struct ClientStartMessage));
582     init->start_flag = htonl (START_FLAG_SCHEDULING);
583     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
584   }
585   do_transmit (sh);
586 }
587
588
589 /**
590  * Initialize the ATS subsystem.
591  *
592  * @param cfg configuration to use
593  * @param suggest_cb notification to call whenever the suggestation changed
594  * @param suggest_cb_cls closure for 'suggest_cb'
595  * @return ats context
596  */
597 struct GNUNET_ATS_SchedulingHandle *
598 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
599                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
600                             void *suggest_cb_cls)
601 {
602   struct GNUNET_ATS_SchedulingHandle *sh;
603
604   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
605   sh->cfg = cfg;
606   sh->suggest_cb = suggest_cb;
607   sh->suggest_cb_cls = suggest_cb_cls;
608   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
609   reconnect (sh);
610   return sh;
611 }
612
613
614 /**
615  * Client is done with ATS scheduling, release resources.
616  *
617  * @param sh handle to release
618  */
619 void
620 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
621 {
622   struct PendingMessage *p;
623
624   while (NULL != (p = sh->pending_head))
625   {
626     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
627     GNUNET_free (p);
628   }
629   if (NULL != sh->client)
630   {
631     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
632     sh->client = NULL;
633   }
634   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
635   {
636     GNUNET_SCHEDULER_cancel (sh->task);
637     sh->task = GNUNET_SCHEDULER_NO_TASK;
638   }
639   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
640   GNUNET_free (sh);
641 }
642
643
644 /**
645  * We would like to establish a new connection with a peer.  ATS
646  * should suggest a good address to begin with.
647  *
648  * @param sh handle
649  * @param peer identity of the peer we need an address for
650  */
651 void
652 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
653                             const struct GNUNET_PeerIdentity *peer)
654 {
655   struct PendingMessage *p;
656   struct RequestAddressMessage *m;
657
658   p = GNUNET_malloc (sizeof (struct PendingMessage) +
659                      sizeof (struct RequestAddressMessage));
660   p->size = sizeof (struct RequestAddressMessage);
661   p->is_init = GNUNET_NO;
662   m = (struct RequestAddressMessage *) &p[1];
663   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
664   m->header.size = htons (sizeof (struct RequestAddressMessage));
665   m->reserved = htonl (0);
666   m->peer = *peer;
667   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
668   do_transmit (sh);
669 }
670
671
672 /**
673  * We would like to stop receiving address updates for this peer
674  *
675  * @param sh handle
676  * @param peer identity of the peer
677  */
678 void
679 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
680                                    const struct GNUNET_PeerIdentity *peer)
681 {
682   struct PendingMessage *p;
683   struct RequestAddressMessage *m;
684
685   p = GNUNET_malloc (sizeof (struct PendingMessage) +
686                      sizeof (struct RequestAddressMessage));
687   p->size = sizeof (struct RequestAddressMessage);
688   p->is_init = GNUNET_NO;
689   m = (struct RequestAddressMessage *) &p[1];
690   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
691   m->header.size = htons (sizeof (struct RequestAddressMessage));
692   m->reserved = htonl (0);
693   m->peer = *peer;
694   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
695   do_transmit (sh);
696 }
697
698 /**
699  * We have updated performance statistics for a given address.  Note
700  * that this function can be called for addresses that are currently
701  * in use as well as addresses that are valid but not actively in use.
702  * Furthermore, the peer may not even be connected to us right now (in
703  * which case the call may be ignored or the information may be stored
704  * for later use).  Update bandwidth assignments.
705  *
706  * @param sh handle
707  * @param address the address
708  * @param session session handle (if available)
709  * @param ats performance data for the address
710  * @param ats_count number of performance records in 'ats'
711  */
712 void
713 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
714                            const struct GNUNET_HELLO_Address *address,
715                            struct Session *session,
716                            const struct GNUNET_ATS_Information *ats,
717                            uint32_t ats_count)
718 {
719   struct PendingMessage *p;
720   struct AddressUpdateMessage *m;
721   struct GNUNET_ATS_Information *am;
722   char *pm;
723   size_t namelen;
724   size_t msize;
725
726   namelen =
727       (address->transport_name ==
728        NULL) ? 0 : strlen (address->transport_name) + 1;
729   msize =
730       sizeof (struct AddressUpdateMessage) + address->address_length +
731       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
732   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
733       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
734       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
735       (ats_count >=
736        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
737   {
738     GNUNET_break (0);
739     return;
740   }
741
742   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
743   p->size = msize;
744   p->is_init = GNUNET_NO;
745   m = (struct AddressUpdateMessage *) &p[1];
746   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
747   m->header.size = htons (msize);
748   m->ats_count = htonl (ats_count);
749   m->peer = address->peer;
750   m->address_length = htons (address->address_length);
751   m->plugin_name_length = htons (namelen);
752   m->session_id = htonl (get_session_id (sh, session, &address->peer));
753   am = (struct GNUNET_ATS_Information *) &m[1];
754   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
755   pm = (char *) &am[ats_count];
756   memcpy (pm, address->address, address->address_length);
757   memcpy (&pm[address->address_length], address->transport_name, namelen);
758   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
759   do_transmit (sh);
760 }
761
762
763 /**
764  * An address is now in use or not used any more.
765  *
766  * @param sh handle
767  * @param address the address
768  * @param session session handle
769  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
770  * if address is not used any more
771  */
772 void
773 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
774                            const struct GNUNET_HELLO_Address *address,
775                            struct Session *session, int in_use)
776 {
777   struct PendingMessage *p;
778   struct AddressUseMessage *m;
779   char *pm;
780   size_t namelen;
781   size_t msize;
782
783   GNUNET_assert (NULL != address);
784   namelen =
785       (address->transport_name ==
786        NULL) ? 0 : strlen (address->transport_name) + 1;
787   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
788   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
789       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
790       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
791   {
792     GNUNET_break (0);
793     return;
794   }
795
796   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
797   p->size = msize;
798   p->is_init = GNUNET_NO;
799   m = (struct AddressUseMessage *) &p[1];
800   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
801   m->header.size = htons (msize);
802   m->peer = address->peer;
803   m->in_use = htons (in_use);
804   m->address_length = htons (address->address_length);
805   m->plugin_name_length = htons (namelen);
806   m->session_id = htonl (get_session_id (sh, session, &address->peer));
807   pm = (char *) &m[1];
808   memcpy (pm, address->address, address->address_length);
809   memcpy (&pm[address->address_length], address->transport_name, namelen);
810   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
811
812   do_transmit (sh);
813 }
814
815 /**
816  * A session got destroyed, stop including it as a valid address.
817  *
818  * @param sh handle
819  * @param address the address
820  * @param session session handle that is no longer valid
821  */
822 void
823 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
824                               const struct GNUNET_HELLO_Address *address,
825                               struct Session *session)
826 {
827   struct PendingMessage *p;
828   struct AddressDestroyedMessage *m;
829   char *pm;
830   size_t namelen;
831   size_t msize;
832   uint32_t session_id;
833
834   GNUNET_assert (address->transport_name != NULL);
835   namelen = strlen (address->transport_name) + 1;
836   GNUNET_assert (namelen > 1);
837   msize =
838       sizeof (struct AddressDestroyedMessage) + address->address_length +
839       namelen;
840   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
841       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
842       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
843   {
844     GNUNET_break (0);
845     return;
846   }
847
848   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
849   p->size = msize;
850   p->is_init = GNUNET_NO;
851   m = (struct AddressDestroyedMessage *) &p[1];
852   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
853   m->header.size = htons (msize);
854   m->reserved = htonl (0);
855   m->peer = address->peer;
856   m->address_length = htons (address->address_length);
857   m->plugin_name_length = htons (namelen);
858   session_id = get_session_id (sh, session, &address->peer);
859   m->session_id = htonl (session_id);
860   pm = (char *) &m[1];
861   memcpy (pm, address->address, address->address_length);
862   memcpy (&pm[address->address_length], address->transport_name, namelen);
863   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
864   do_transmit (sh);
865   remove_session (sh, session_id, &address->peer);
866 }
867
868 /* end of ats_api_scheduling.c */