7e44a8660b0da5d1033ec90a28f3bdfb38846e78
[oweals/gnunet.git] / src / ats / ats_api_scheduling.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010-2015 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 /**
31  * How frequently do we scan the interfaces for changes to the addresses?
32  */
33 #define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
34
35
36 /**
37  * Session ID we use if there is no session / slot.
38  */
39 #define NOT_FOUND 0
40
41
42 /**
43  * Information we track per address.
44  * FIXME: but what about *incoming* connections?
45  *        "address" tells us about those, those
46  *        are only valid while we have a session.
47  *        Need to clarify all this!!!
48  */
49 struct GNUNET_ATS_AddressRecord
50 {
51   /**
52    * Address data.
53    */
54   struct GNUNET_HELLO_Address *address;
55
56   /**
57    * Session handle.  NULL if we have an address but no
58    * active session for this address.
59    */
60   struct Session *session;
61
62   /**
63    * Array with performance data about the address.
64    */
65   struct GNUNET_ATS_Information *ats;
66
67   /**
68    * Number of entries in @e ats.
69    */
70   uint32_t ats_count;
71
72   /**
73    * Which slot (index) in the session array does
74    * this record correspond to?  FIXME:
75    * FIXME: a linear search on this is really crappy!
76    * Maybe switch to a 64-bit global counter and be
77    * done with it?  Or does that then cause too much
78    * trouble on the ATS-service side?
79    */
80   uint32_t slot;
81
82   /**
83    * Is this address currently in use?
84    * FIXME: document what "in use" means, and why it
85    * is important!
86    */
87   int in_use;
88
89   /**
90    * We're about to destroy this address record, just ATS does
91    * not know this yet.
92    */
93   int in_destroy;
94 };
95
96
97 /**
98  * We keep a list of our local networks so we can answer
99  * LAN vs. WAN questions.  Note: WLAN is not detected yet.
100  * (maybe we can do that heuristically based on interface
101  * name in the future?)
102  */
103 struct ATS_Network
104 {
105   /**
106    * Kept in a DLL.
107    */
108   struct ATS_Network *next;
109
110   /**
111    * Kept in a DLL.
112    */
113   struct ATS_Network *prev;
114
115   /**
116    * Network address.
117    */
118   struct sockaddr *network;
119
120   /**
121    * Netmask to determine what is in the LAN.
122    */
123   struct sockaddr *netmask;
124
125   /**
126    * How long are @e network and @e netmask?
127    */
128   socklen_t length;
129 };
130
131
132 /**
133  * Handle for ATS address suggestion requests.
134  */
135 struct GNUNET_ATS_SuggestHandle
136 {
137   /**
138    * ID of the peer for which address suggestion was requested.
139    */
140   struct GNUNET_PeerIdentity id;
141 };
142
143
144 /**
145  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
146  */
147 struct GNUNET_ATS_SchedulingHandle
148 {
149
150   /**
151    * Our configuration.
152    */
153   const struct GNUNET_CONFIGURATION_Handle *cfg;
154
155   /**
156    * Callback to invoke on suggestions.
157    */
158   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
159
160   /**
161    * Closure for @e suggest_cb.
162    */
163   void *suggest_cb_cls;
164
165   /**
166    * Map with the identities of all the peers for which we would
167    * like to have address suggestions.  The key is the PID, the
168    * value is currently the `struct GNUNET_ATS_SuggestHandle`
169    */
170   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
171
172   /**
173    * Connection to ATS service.
174    */
175   struct GNUNET_CLIENT_Connection *client;
176
177   /**
178    * Message queue for sending requests to the ATS service.
179    */
180   struct GNUNET_MQ_Handle *mq;
181
182   /**
183    * Head of LAN networks list.
184    */
185   struct ATS_Network *net_head;
186
187   /**
188    * Tail of LAN networks list.
189    */
190   struct ATS_Network *net_tail;
191
192   /**
193    * Array of session objects (we need to translate them to numbers and back
194    * for the protocol; the offset in the array is the session number on the
195    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
196    * Unused entries are also NULL.
197    */
198   struct GNUNET_ATS_AddressRecord **session_array;
199
200   /**
201    * Task to trigger reconnect.
202    */
203   struct GNUNET_SCHEDULER_Task *task;
204
205   /**
206    * Task for periodically refreshing our LAN network list.
207    */
208   struct GNUNET_SCHEDULER_Task *interface_task;
209
210   /**
211    * Size of the @e session_array.
212    */
213   unsigned int session_array_size;
214
215 };
216
217
218 /**
219  * Re-establish the connection to the ATS service.
220  *
221  * @param sh handle to use to re-connect.
222  */
223 static void
224 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
225
226
227 /**
228  * Re-establish the connection to the ATS service.
229  *
230  * @param cls handle to use to re-connect.
231  * @param tc scheduler context
232  */
233 static void
234 reconnect_task (void *cls,
235                 const struct GNUNET_SCHEDULER_TaskContext *tc)
236 {
237   struct GNUNET_ATS_SchedulingHandle *sh = cls;
238
239   sh->task = NULL;
240   reconnect (sh);
241 }
242
243
244 /**
245  * Disconnect from ATS and then reconnect.
246  *
247  * @param sh our handle
248  */
249 static void
250 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
251 {
252   if (NULL != sh->mq)
253   {
254     GNUNET_MQ_destroy (sh->mq);
255     sh->mq = NULL;
256   }
257   if (NULL != sh->client)
258   {
259     GNUNET_CLIENT_disconnect (sh->client);
260     sh->client = NULL;
261   }
262   sh->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
263                                            &reconnect_task,
264                                            sh);
265 }
266
267
268 /**
269  * Find the session object corresponding to the given session ID.
270  *
271  * @param sh our handle
272  * @param session_id current session ID
273  * @param peer peer the session belongs to
274  * @return the session object (or NULL)
275  */
276 static struct GNUNET_ATS_AddressRecord *
277 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
278               uint32_t session_id,
279               const struct GNUNET_PeerIdentity *peer)
280 {
281   struct GNUNET_ATS_AddressRecord *ar;
282
283   if (session_id >= sh->session_array_size)
284   {
285     GNUNET_break (0);
286     return NULL;
287   }
288   if (0 == session_id)
289     return NULL;
290   ar = sh->session_array[session_id];
291   if (NULL == ar)
292   {
293     GNUNET_break (0);
294     return NULL;
295   }
296   if (NULL == ar->address)
297   {
298     /* address was destroyed in the meantime, this can happen
299        as we communicate asynchronously with the ATS service. */
300     return NULL;
301   }
302   if (0 != memcmp (peer,
303                    &ar->address->peer,
304                    sizeof (struct GNUNET_PeerIdentity)))
305   {
306     GNUNET_break (0);
307     force_reconnect (sh);
308     return NULL;
309   }
310   return ar;
311 }
312
313
314 /**
315  * Get an available session ID.
316  *
317  * @param sh our handle
318  * @return an unused slot, but never NOT_FOUND (0)
319  */
320 static uint32_t
321 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh)
322 {
323   static uint32_t off;
324   uint32_t i;
325
326   i = 0;
327   while ( ( (NOT_FOUND == off) ||
328             (NULL != sh->session_array[off % sh->session_array_size]) ) &&
329           (i < sh->session_array_size) )
330   {
331     off++;
332     i++;
333   }
334   if ( (NOT_FOUND != off) &&
335        (NULL == sh->session_array[off % sh->session_array_size]) )
336     return off;
337   i = sh->session_array_size;
338   GNUNET_array_grow (sh->session_array,
339                      sh->session_array_size,
340                      sh->session_array_size * 2);
341   return i;
342 }
343
344
345 /**
346  * Get the ID for the given session object.
347  *
348  * @param sh our handle
349  * @param session session object
350  * @param address the address we are looking for
351  * @return the session id or NOT_FOUND for error
352  */
353 static uint32_t
354 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
355                  struct Session *session,
356                  const struct GNUNET_HELLO_Address *address)
357 {
358   uint32_t i;
359
360   if (NULL == address)
361   {
362     GNUNET_break (0);
363     return NOT_FOUND;
364   }
365   for (i = 1; i < sh->session_array_size; i++)
366     if ( (NULL != sh->session_array[i]) &&
367          ( (session == sh->session_array[i]->session) ||
368            (NULL == sh->session_array[i]->session) ) &&
369          (0 == GNUNET_HELLO_address_cmp (address,
370                                          sh->session_array[i]->address)) )
371       return i;
372   return NOT_FOUND;
373 }
374
375
376 /**
377  * Release the session slot from the session table (ATS service is
378  * also done using it).
379  *
380  * @param sh our handle
381  * @param session_id identifies session that is no longer valid
382  */
383 static void
384 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
385                  uint32_t session_id)
386 {
387   struct GNUNET_ATS_AddressRecord *ar;
388
389   if (NOT_FOUND == session_id)
390     return;
391   if (session_id >= sh->session_array_size)
392   {
393     GNUNET_break (0);
394     force_reconnect (sh);
395     return;
396   }
397   /* this slot should have been removed from remove_session before */
398   ar = sh->session_array[session_id];
399   if (NULL != ar->session)
400   {
401     GNUNET_break (0);
402     force_reconnect (sh);
403     return;
404   }
405   GNUNET_HELLO_address_free (ar->address);
406   GNUNET_free (ar);
407   sh->session_array[session_id] = NULL;
408 }
409
410
411 /**
412  * Type of a function to call when we receive a session release
413  * message from the service.
414  *
415  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
416  * @param msg message received, NULL on timeout or fatal error
417  */
418 static void
419 process_ats_session_release_message (void *cls,
420                                      const struct GNUNET_MessageHeader *msg)
421 {
422   struct GNUNET_ATS_SchedulingHandle *sh = cls;
423   const struct SessionReleaseMessage *srm;
424
425   srm = (const struct SessionReleaseMessage *) msg;
426   /* FIXME: peer field in srm not necessary anymore */
427   release_session (sh,
428                    ntohl (srm->session_id));
429 }
430
431
432 /**
433  * Type of a function to call when we receive a address suggestion
434  * message from the service.
435  *
436  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
437  * @param msg message received, NULL on timeout or fatal error
438  */
439 static void
440 process_ats_address_suggestion_message (void *cls,
441                                         const struct GNUNET_MessageHeader *msg)
442 {
443   struct GNUNET_ATS_SchedulingHandle *sh = cls;
444   const struct AddressSuggestionMessage *m;
445   const struct GNUNET_ATS_Information *atsi;
446   const char *plugin_address;
447   const char *plugin_name;
448   uint16_t plugin_address_length;
449   uint16_t plugin_name_length;
450   uint32_t ats_count;
451   struct GNUNET_HELLO_Address address;
452   struct Session *s;
453   struct GNUNET_ATS_AddressRecord *ar;
454
455   if (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage))
456   {
457     GNUNET_break (0);
458     force_reconnect (sh);
459     return;
460   }
461   /* FIXME: we have all the address details, no need for ATS
462      to send those back to us any longer! */
463   m = (const struct AddressSuggestionMessage *) msg;
464   ats_count = ntohl (m->ats_count);
465   plugin_address_length = ntohs (m->address_length);
466   atsi = (const struct GNUNET_ATS_Information *) &m[1];
467   plugin_address = (const char *) &atsi[ats_count];
468   plugin_name = &plugin_address[plugin_address_length];
469   plugin_name_length = ntohs (m->plugin_name_length);
470   if ((plugin_address_length + plugin_name_length +
471        ats_count * sizeof (struct GNUNET_ATS_Information) +
472        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
473       (ats_count >
474        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
475       || (plugin_name[plugin_name_length - 1] != '\0'))
476   {
477     GNUNET_break (0);
478     force_reconnect (sh);
479     return;
480   }
481   uint32_t session_id = ntohl (m->session_id);
482
483   if (0 == session_id)
484   {
485     GNUNET_break (0);
486     force_reconnect (sh);
487     return;
488   }
489   ar = find_session (sh, session_id, &m->peer);
490   if (NULL == ar)
491   {
492     GNUNET_break (0);
493     force_reconnect (sh);
494     return;
495   }
496   s = ar->session;
497   if (NULL == sh->suggest_cb)
498     return;
499   if ( (GNUNET_YES == ar->in_destroy) &&
500        ( (0 != ntohl (m->bandwidth_out.value__)) ||
501          (0 != ntohl (m->bandwidth_in.value__)) ) )
502   {
503     /* ignore suggestion, as this address is dying */
504     return;
505   }
506   address.peer = m->peer;
507   address.address = plugin_address;
508   address.address_length = plugin_address_length;
509   address.transport_name = plugin_name;
510   address.local_info = ntohl(m->address_local_info);
511
512   if ((s == NULL) && (0 == address.address_length))
513   {
514     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
516                 GNUNET_i2s (&address.peer),
517                 address.transport_name,
518                 plugin_address_length,
519                 session_id);
520     GNUNET_break_op (0);
521     return;
522   }
523   sh->suggest_cb (sh->suggest_cb_cls,
524                   &m->peer,
525                   &address,
526                   s,
527                   m->bandwidth_out,
528                   m->bandwidth_in,
529                   atsi, ats_count);
530 }
531
532
533 /**
534  * We encountered an error handling the MQ to the
535  * ATS service.  Reconnect.
536  *
537  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
538  * @param error details about the error
539  */
540 static void
541 error_handler (void *cls,
542                enum GNUNET_MQ_Error error)
543 {
544   struct GNUNET_ATS_SchedulingHandle *sh = cls;
545
546   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
547               "ATS connection died (code %d), reconnecting\n",
548               (int) error);
549   force_reconnect (sh);
550 }
551
552
553 /**
554  * Generate the "AddressUpdateMessage" for the given
555  * session record.
556  *
557  * @param sh the scheduling handle to use for transmission
558  * @param ar the address to inform the ATS service about
559  * @param msg_type the message type to use when sending the message
560  *
561  * FIXME: maybe overloading with msg_type was not the best idea here...
562  */
563 static void
564 send_add_address_message (struct GNUNET_ATS_SchedulingHandle *sh,
565                           const struct GNUNET_ATS_AddressRecord *ar,
566                           uint16_t msg_type)
567 {
568   struct GNUNET_MQ_Envelope *ev;
569   struct AddressUpdateMessage *m;
570   struct GNUNET_ATS_Information *am;
571   char *pm;
572   size_t namelen;
573   size_t msize;
574
575   if (NULL == sh->mq)
576     return; /* disconnected, skip for now */
577   namelen = (NULL == ar->address->transport_name)
578     ? 0
579     : strlen (ar->address->transport_name) + 1;
580   msize = ar->address->address_length +
581     ar->ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
582
583   ev = GNUNET_MQ_msg_extra (m, msize, msg_type);
584   m->ats_count = htonl (ar->ats_count);
585   m->peer = ar->address->peer;
586   m->address_length = htons (ar->address->address_length);
587   m->address_local_info = htonl ((uint32_t) ar->address->local_info);
588   m->plugin_name_length = htons (namelen);
589   m->session_id = htonl (ar->slot);
590
591   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592               "Adding address for peer `%s', plugin `%s', session %p id %u\n",
593               GNUNET_i2s (&ar->address->peer),
594               ar->address->transport_name,
595               ar->session,
596               ar->slot);
597   am = (struct GNUNET_ATS_Information *) &m[1];
598   memcpy (am,
599           ar->ats,
600           ar->ats_count * sizeof (struct GNUNET_ATS_Information));
601   pm = (char *) &am[ar->ats_count];
602   memcpy (pm,
603           ar->address->address,
604           ar->address->address_length);
605   if (NULL != ar->address->transport_name)
606     memcpy (&pm[ar->address->address_length],
607             ar->address->transport_name,
608             namelen);
609   GNUNET_MQ_send (sh->mq, ev);
610 }
611
612
613 /**
614  * Re-establish the connection to the ATS service.
615  *
616  * @param sh handle to use to re-connect.
617  */
618 static void
619 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
620 {
621   static const struct GNUNET_MQ_MessageHandler handlers[] =
622     { { &process_ats_session_release_message,
623         GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
624         sizeof (struct SessionReleaseMessage) },
625       { &process_ats_address_suggestion_message,
626         GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
627         0 },
628       { NULL, 0, 0 } };
629   struct GNUNET_MQ_Envelope *ev;
630   struct ClientStartMessage *init;
631
632   GNUNET_assert (NULL == sh->client);
633   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
634   if (NULL == sh->client)
635   {
636     force_reconnect (sh);
637     return;
638   }
639   sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
640                                                   handlers,
641                                                   &error_handler,
642                                                   sh);
643   ev = GNUNET_MQ_msg (init,
644                       GNUNET_MESSAGE_TYPE_ATS_START);
645   init->start_flag = htonl (START_FLAG_SCHEDULING);
646   GNUNET_MQ_send (sh->mq, ev);
647   // FIXME: iterate over addresses...
648   // FIXME: iterate over peermap for address suggestion requests!
649 }
650
651
652 /**
653  * Delete all entries from the current network list.
654  *
655  * @param sh scheduling handle to clean up
656  */
657 static void
658 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
659 {
660   struct ATS_Network *cur;
661
662   while (NULL != (cur = sh->net_head))
663   {
664     GNUNET_CONTAINER_DLL_remove (sh->net_head,
665                                  sh->net_tail,
666                                  cur);
667     GNUNET_free (cur);
668   }
669 }
670
671
672 /**
673  * Function invoked for each interface found.  Adds the interface's
674  * network addresses to the respective DLL, so we can distinguish
675  * between LAN and WAN.
676  *
677  * @param cls closure
678  * @param name name of the interface (can be NULL for unknown)
679  * @param isDefault is this presumably the default interface
680  * @param addr address of this interface (can be NULL for unknown or unassigned)
681  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
682  * @param netmask the network mask (can be NULL for unknown or unassigned)
683  * @param addrlen length of the address
684  * @return #GNUNET_OK to continue iteration
685  */
686 static int
687 interface_proc (void *cls,
688                 const char *name,
689                 int isDefault,
690                 const struct sockaddr *addr,
691                 const struct sockaddr *broadcast_addr,
692                 const struct sockaddr *netmask,
693                 socklen_t addrlen)
694 {
695   struct GNUNET_ATS_SchedulingHandle *sh = cls;
696   /* Calculate network */
697   struct ATS_Network *net = NULL;
698
699   /* Skipping IPv4 loopback addresses since we have special check  */
700   if  (addr->sa_family == AF_INET)
701   {
702     const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
703
704     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
705        return GNUNET_OK;
706   }
707   /* Skipping IPv6 loopback addresses since we have special check  */
708   if  (addr->sa_family == AF_INET6)
709   {
710     const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
711     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
712       return GNUNET_OK;
713   }
714
715   if (addr->sa_family == AF_INET)
716   {
717     const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
718     const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
719     struct sockaddr_in *tmp;
720     struct sockaddr_in network4;
721
722     net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
723     tmp = (struct sockaddr_in *) &net[1];
724     net->network = (struct sockaddr *) &tmp[0];
725     net->netmask = (struct sockaddr *) &tmp[1];
726     net->length = addrlen;
727
728     memset (&network4, 0, sizeof (network4));
729     network4.sin_family = AF_INET;
730 #if HAVE_SOCKADDR_IN_SIN_LEN
731     network4.sin_len = sizeof (network4);
732 #endif
733     network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
734
735     memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
736     memcpy (net->network, &network4, sizeof (struct sockaddr_in));
737   }
738
739   if (addr->sa_family == AF_INET6)
740   {
741     const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
742     const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
743     struct sockaddr_in6 * tmp;
744     struct sockaddr_in6 network6;
745
746     net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
747     tmp = (struct sockaddr_in6 *) &net[1];
748     net->network = (struct sockaddr *) &tmp[0];
749     net->netmask = (struct sockaddr *) &tmp[1];
750     net->length = addrlen;
751
752     memset (&network6, 0, sizeof (network6));
753     network6.sin6_family = AF_INET6;
754 #if HAVE_SOCKADDR_IN_SIN_LEN
755     network6.sin6_len = sizeof (network6);
756 #endif
757     unsigned int c = 0;
758     uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
759     uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
760     uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
761     for (c = 0; c < 4; c++)
762       net_elem[c] = addr_elem[c] & mask_elem[c];
763
764     memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
765     memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
766   }
767   if (NULL == net)
768     return GNUNET_OK; /* odd / unsupported address family */
769
770   /* Store in list */
771 #if VERBOSE_ATS
772   char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
773   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774               "Adding network `%s', netmask `%s'\n",
775               GNUNET_a2s ((struct sockaddr *) net->network,
776                           addrlen),
777               netmask);
778   GNUNET_free (netmask);
779 #endif
780   GNUNET_CONTAINER_DLL_insert (sh->net_head,
781                                sh->net_tail,
782                                net);
783
784   return GNUNET_OK;
785 }
786
787
788 /**
789  * Periodically get list of network addresses from our interfaces.
790  *
791  * @param cls closure
792  * @param tc Task context
793  */
794 static void
795 get_addresses (void *cls,
796                const struct GNUNET_SCHEDULER_TaskContext *tc)
797 {
798   struct GNUNET_ATS_SchedulingHandle *sh = cls;
799
800   sh->interface_task = NULL;
801   delete_networks (sh);
802   GNUNET_OS_network_interfaces_list (&interface_proc,
803                                      sh);
804   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
805                                                      &get_addresses,
806                                                      sh);
807 }
808
809
810 /**
811  * Convert a `enum GNUNET_ATS_Network_Type` to a string
812  *
813  * @param net the network type
814  * @return a string or NULL if invalid
815  */
816 const char *
817 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
818 {
819   switch (net)
820     {
821     case GNUNET_ATS_NET_UNSPECIFIED:
822       return "UNSPECIFIED";
823     case GNUNET_ATS_NET_LOOPBACK:
824       return "LOOPBACK";
825     case GNUNET_ATS_NET_LAN:
826       return "LAN";
827     case GNUNET_ATS_NET_WAN:
828       return "WAN";
829     case GNUNET_ATS_NET_WLAN:
830       return "WLAN";
831     case GNUNET_ATS_NET_BT:
832       return "BLUETOOTH";
833     default:
834       return NULL;
835     }
836 }
837
838
839 /**
840  * Convert a ATS property to a string
841  *
842  * @param type the property type
843  * @return a string or NULL if invalid
844  */
845 const char *
846 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
847 {
848   switch (type)
849   {
850   case GNUNET_ATS_ARRAY_TERMINATOR:
851     return "TERMINATOR";
852   case GNUNET_ATS_UTILIZATION_OUT:
853     return "UTILIZATION_UP";
854   case GNUNET_ATS_UTILIZATION_IN:
855     return "UTILIZATION_DOWN";
856   case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
857     return "UTILIZATION_PAYLOAD_UP";
858   case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
859     return "UTILIZATION_PAYLOAD_DOWN";
860   case GNUNET_ATS_NETWORK_TYPE:
861     return "NETWORK_TYPE";
862   case GNUNET_ATS_QUALITY_NET_DELAY:
863     return "DELAY";
864   case GNUNET_ATS_QUALITY_NET_DISTANCE:
865     return "DISTANCE";
866   case GNUNET_ATS_COST_WAN:
867     return "COST_WAN";
868   case GNUNET_ATS_COST_LAN:
869     return "COST_LAN";
870   case GNUNET_ATS_COST_WLAN:
871     return "COST_WLAN";
872   default:
873     return NULL;
874   }
875 }
876
877
878 /**
879  * Returns where the address is located: LAN or WAN or ...
880  *
881  * @param sh the scheduling handle
882  * @param addr address
883  * @param addrlen address length
884  * @return type of the network the address belongs to
885  */
886 enum GNUNET_ATS_Network_Type
887 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle *sh,
888                              const struct sockaddr *addr,
889                              socklen_t addrlen)
890 {
891   struct ATS_Network *cur = sh->net_head;
892   enum GNUNET_ATS_NetworkType type = GNUNET_ATS_NET_UNSPECIFIED;
893
894   switch (addr->sa_family)
895     {
896     case AF_UNIX:
897       type = GNUNET_ATS_NET_LOOPBACK;
898       break;
899     case AF_INET:
900       {
901         const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
902
903         if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
904           type = GNUNET_ATS_NET_LOOPBACK;
905         break;
906       }
907     case AF_INET6:
908       {
909         const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
910
911         if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
912           type = GNUNET_ATS_NET_LOOPBACK;
913         break;
914       }
915     default:
916       GNUNET_break (0);
917       break;
918    }
919
920   /* Check local networks */
921   while ((NULL != cur) && (GNUNET_ATS_NET_UNSPECIFIED == type))
922   {
923     if (addrlen != cur->length)
924     {
925       cur = cur->next;
926       continue;
927     }
928     if (addr->sa_family == AF_INET)
929     {
930       const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
931       const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network;
932       const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask;
933
934       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
935         type = GNUNET_ATS_NET_LAN;
936     }
937     if (addr->sa_family == AF_INET6)
938     {
939       const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
940       const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network;
941       const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask;
942
943       int res = GNUNET_YES;
944       int c = 0;
945       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
946       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
947       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
948       for (c = 0; c < 4; c++)
949         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
950           res = GNUNET_NO;
951
952       if (res == GNUNET_YES)
953         type = GNUNET_ATS_NET_LAN;
954     }
955     cur = cur->next;
956   }
957
958   /* no local network found for this address, default: WAN */
959   if (type == GNUNET_ATS_NET_UNSPECIFIED)
960     type = GNUNET_ATS_NET_WAN;
961   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
962                    "ats-scheduling-api",
963                    "`%s' is in network `%s'\n",
964                    GNUNET_a2s (addr,
965                                addrlen),
966                    GNUNET_ATS_print_network_type (type));
967   return type;
968 }
969
970
971 /**
972  * Initialize the ATS subsystem.
973  *
974  * @param cfg configuration to use
975  * @param suggest_cb notification to call whenever the suggestation changed
976  * @param suggest_cb_cls closure for @a suggest_cb
977  * @return ats context
978  */
979 struct GNUNET_ATS_SchedulingHandle *
980 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
981                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
982                             void *suggest_cb_cls)
983 {
984   struct GNUNET_ATS_SchedulingHandle *sh;
985
986   sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
987   sh->cfg = cfg;
988   sh->suggest_cb = suggest_cb;
989   sh->suggest_cb_cls = suggest_cb_cls;
990   GNUNET_array_grow (sh->session_array,
991                      sh->session_array_size,
992                      4);
993   sh->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
994                                                            GNUNET_YES);
995   GNUNET_OS_network_interfaces_list (&interface_proc,
996                                      sh);
997   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
998                                                      &get_addresses,
999                                                      sh);
1000   reconnect (sh);
1001   return sh;
1002 }
1003
1004
1005 /**
1006  * Function called to free all `struct GNUNET_ATS_SuggestHandles`
1007  * in the map.
1008  *
1009  * @param cls NULL
1010  * @param key the key
1011  * @param value the value to free
1012  * @return #GNUNET_OK (continue to iterate)
1013  */
1014 static int
1015 free_sug_handle (void *cls,
1016                  const struct GNUNET_PeerIdentity *key,
1017                  void *value)
1018 {
1019   struct GNUNET_ATS_SuggestHandle *cur = value;
1020
1021   GNUNET_free (cur);
1022   return GNUNET_OK;
1023 }
1024
1025
1026
1027 /**
1028  * Client is done with ATS scheduling, release resources.
1029  *
1030  * @param sh handle to release
1031  */
1032 void
1033 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1034 {
1035   if (NULL != sh->mq)
1036   {
1037     GNUNET_MQ_destroy (sh->mq);
1038     sh->mq = NULL;
1039   }
1040   if (NULL != sh->client)
1041   {
1042     GNUNET_CLIENT_disconnect (sh->client);
1043     sh->client = NULL;
1044   }
1045   if (NULL != sh->task)
1046   {
1047     GNUNET_SCHEDULER_cancel (sh->task);
1048     sh->task = NULL;
1049   }
1050   GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
1051                                          &free_sug_handle,
1052                                          NULL);
1053   GNUNET_CONTAINER_multipeermap_destroy (sh->sug_requests);
1054   if (NULL != sh->interface_task)
1055   {
1056     GNUNET_SCHEDULER_cancel (sh->interface_task);
1057     sh->interface_task = NULL;
1058   }
1059   delete_networks (sh);
1060   GNUNET_array_grow (sh->session_array,
1061                      sh->session_array_size,
1062                      0);
1063   GNUNET_free (sh);
1064 }
1065
1066
1067 /**
1068  * We would like to reset the address suggestion block time for this
1069  * peer
1070  *
1071  * @param sh handle
1072  * @param peer identity of the peer we want to reset
1073  */
1074 void
1075 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1076                           const struct GNUNET_PeerIdentity *peer)
1077 {
1078   struct GNUNET_MQ_Envelope *ev;
1079   struct ResetBackoffMessage *m;
1080
1081   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1082   m->reserved = htonl (0);
1083   m->peer = *peer;
1084   GNUNET_MQ_send (sh->mq, ev);
1085 }
1086
1087
1088 /**
1089  * We would like to receive address suggestions for a peer. ATS will
1090  * respond with a call to the continuation immediately containing an address or
1091  * no address if none is available. ATS can suggest more addresses until we call
1092  * #GNUNET_ATS_suggest_address_cancel().
1093  *
1094  * @param sh handle
1095  * @param peer identity of the peer we need an address for
1096  * @return suggest handle, NULL if a request is already pending
1097  */
1098 struct GNUNET_ATS_SuggestHandle *
1099 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1100                             const struct GNUNET_PeerIdentity *peer)
1101 {
1102   struct GNUNET_MQ_Envelope *ev;
1103   struct RequestAddressMessage *m;
1104   struct GNUNET_ATS_SuggestHandle *s;
1105
1106   s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1107   s->id = *peer;
1108   if (GNUNET_OK !=
1109       GNUNET_CONTAINER_multipeermap_put (sh->sug_requests,
1110                                          &s->id,
1111                                          s,
1112                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1113   {
1114     GNUNET_break (0);
1115     return NULL;
1116   }
1117   if (NULL == sh->mq)
1118     return s;
1119   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1120   m->reserved = htonl (0);
1121   m->peer = *peer;
1122   GNUNET_MQ_send (sh->mq, ev);
1123   return s;
1124 }
1125
1126
1127 /**
1128  * We would like to stop receiving address updates for this peer
1129  *
1130  * @param sh handle
1131  * @param peer identity of the peer
1132  */
1133 void
1134 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1135                                    const struct GNUNET_PeerIdentity *peer)
1136 {
1137   struct GNUNET_MQ_Envelope *ev;
1138   struct RequestAddressMessage *m;
1139   struct GNUNET_ATS_SuggestHandle *s;
1140
1141   s = GNUNET_CONTAINER_multipeermap_get (sh->sug_requests,
1142                                          peer);
1143   if (NULL == s)
1144   {
1145     GNUNET_break (0);
1146     return;
1147   }
1148   GNUNET_assert (GNUNET_OK ==
1149                  GNUNET_CONTAINER_multipeermap_remove (sh->sug_requests,
1150                                                        &s->id,
1151                                                        s));
1152   GNUNET_free (s);
1153   if (NULL == sh->mq)
1154     return;
1155   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1156   m->reserved = htonl (0);
1157   m->peer = *peer;
1158   GNUNET_MQ_send (sh->mq, ev);
1159 }
1160
1161
1162 /**
1163  * Test if a address and a session is known to ATS
1164  *
1165  * @param sh the scheduling handle
1166  * @param address the address
1167  * @param session the session
1168  * @return #GNUNET_YES or #GNUNET_NO
1169  */
1170 int
1171 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1172                           const struct GNUNET_HELLO_Address *address,
1173                           struct Session *session)
1174 {
1175   if (NULL == session)
1176     return GNUNET_NO;
1177   if (NOT_FOUND != find_session_id (sh,
1178                                     session,
1179                                     address))
1180     return GNUNET_YES;  /* Exists */
1181   return GNUNET_NO;
1182 }
1183
1184
1185 /**
1186  * We have a new address ATS should know. Addresses have to be added
1187  * with this function before they can be: updated, set in use and
1188  * destroyed.
1189  *
1190  * @param sh handle
1191  * @param address the address
1192  * @param session session handle, can be NULL
1193  * @param ats performance data for the address
1194  * @param ats_count number of performance records in @a ats
1195  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1196  */
1197 int
1198 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1199                         const struct GNUNET_HELLO_Address *address,
1200                         struct Session *session,
1201                         const struct GNUNET_ATS_Information *ats,
1202                         uint32_t ats_count)
1203 {
1204   struct GNUNET_ATS_AddressRecord *ar;
1205   size_t namelen;
1206   size_t msize;
1207   uint32_t s;
1208
1209   if (NULL == address)
1210   {
1211     /* we need a valid address */
1212     GNUNET_break (0);
1213     return GNUNET_SYSERR;
1214   }
1215   namelen = (NULL == address->transport_name)
1216     ? 0
1217     : strlen (address->transport_name) + 1;
1218   msize = address->address_length +
1219     ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1220   if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1221       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1222       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1223       (ats_count >=
1224        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1225   {
1226     /* address too large for us, this should not happen */
1227     GNUNET_break (0);
1228     return GNUNET_SYSERR;
1229   }
1230
1231   if (NOT_FOUND != find_session_id (sh, session, address))
1232   {
1233     /* Already existing, nothing todo, but this should not happen */
1234     GNUNET_break (0);
1235     return GNUNET_SYSERR;
1236   }
1237   s = find_empty_session_slot (sh);
1238   ar = GNUNET_new (struct GNUNET_ATS_AddressRecord);
1239   ar->slot = s;
1240   ar->session = session;
1241   ar->address = GNUNET_HELLO_address_copy (address);
1242   GNUNET_array_grow (ar->ats,
1243                      ar->ats_count,
1244                      ats_count);
1245   memcpy (ar->ats, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1246   sh->session_array[s] = ar;
1247   send_add_address_message (sh, ar, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1248   return GNUNET_OK;
1249 }
1250
1251
1252 /**
1253  * We have updated performance statistics for a given address.  Note
1254  * that this function can be called for addresses that are currently
1255  * in use as well as addresses that are valid but not actively in use.
1256  * Furthermore, the peer may not even be connected to us right now (in
1257  * which case the call may be ignored or the information may be stored
1258  * for later use).  Update bandwidth assignments.
1259  *
1260  * FIXME: change API so we do not have to do the linear search!
1261  *
1262  * @param sh handle
1263  * @param address the address
1264  * @param session session handle, can be NULL
1265  * @param ats performance data for the address
1266  * @param ats_count number of performance records in @a ats
1267  * @return #GNUNET_YES on success, #GNUNET_NO if address or session are unknown,
1268  * #GNUNET_SYSERR on hard failure
1269  */
1270 int
1271 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1272                            const struct GNUNET_HELLO_Address *address,
1273                            struct Session *session,
1274                            const struct GNUNET_ATS_Information *ats,
1275                            uint32_t ats_count)
1276 {
1277   uint32_t s;
1278   struct GNUNET_ATS_AddressRecord *ar;
1279
1280   s = find_session_id (sh, session, address);
1281   if (NOT_FOUND == s)
1282   {
1283     GNUNET_break (0);
1284     return GNUNET_NO;
1285   }
1286   ar = sh->session_array[s];
1287   GNUNET_array_grow (ar->ats,
1288                      ar->ats_count,
1289                      ats_count);
1290   memcpy (ar->ats,
1291           ats,
1292           ats_count * sizeof (struct GNUNET_ATS_Information));
1293   ar->session = session;
1294   send_add_address_message (sh, ar, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1295   return GNUNET_YES;
1296 }
1297
1298
1299 /**
1300  * An address is now in use or not used any more.
1301  *
1302  * @param sh handle
1303  * @param address the address
1304  * @param session session handle, can be NULL
1305  * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1306  * if address is not used any more
1307  */
1308 void
1309 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1310                            const struct GNUNET_HELLO_Address *address,
1311                            struct Session *session,
1312                            int in_use)
1313 {
1314   struct GNUNET_MQ_Envelope *ev;
1315   struct AddressUseMessage *m;
1316   struct GNUNET_ATS_AddressRecord *ar;
1317   char *pm;
1318   size_t namelen;
1319   size_t msize;
1320   uint32_t s = 0;
1321
1322   s = find_session_id (sh, session, address);
1323   if (s == NOT_FOUND)
1324   {
1325     /* trying to set unknown address to NO */
1326     GNUNET_break (0);
1327     return;
1328   }
1329   ar = sh->session_array[s];
1330   ar->in_use = in_use;
1331   namelen = (NULL == address->transport_name)
1332     ? 0
1333     : strlen (address->transport_name) + 1;
1334   msize = address->address_length + namelen;
1335
1336   ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1337   m->peer = address->peer;
1338   m->in_use = htons (in_use);
1339   m->address_length = htons (address->address_length);
1340   m->address_local_info = htonl ((uint32_t) address->local_info);
1341   m->plugin_name_length = htons (namelen);
1342
1343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1344               "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1345               (GNUNET_YES == in_use) ? "YES" : "NO",
1346               GNUNET_i2s (&address->peer),
1347               address->transport_name,
1348               session);
1349
1350   m->session_id = htonl (s);
1351   pm = (char *) &m[1];
1352   /* FIXME: no need to send the address data */
1353   memcpy (pm, address->address, address->address_length);
1354   memcpy (&pm[address->address_length], address->transport_name, namelen);
1355   GNUNET_MQ_send (sh->mq, ev);
1356 }
1357
1358
1359 /**
1360  * An address got destroyed, stop including it as a valid address.
1361  *
1362  * If a session is given, only the session will be removed, if no session is
1363  * given the full address will be deleted.
1364  *
1365  * FIXME: the above sentence already indicates that this API is a
1366  * mess and troublesome. FIX IT!
1367  *
1368  * @param sh handle
1369  * @param address the address
1370  * @param session session handle that is no longer valid, can be NULL
1371  */
1372 void
1373 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1374                               const struct GNUNET_HELLO_Address *address,
1375                               struct Session *session)
1376 {
1377   uint32_t s;
1378   struct GNUNET_MQ_Envelope *ev;
1379   struct AddressDestroyedMessage *m;
1380   struct GNUNET_ATS_AddressRecord *ar;
1381   char *pm;
1382   size_t namelen;
1383   size_t msize;
1384
1385   s = find_session_id (sh, session, address);
1386   if (NOT_FOUND == s)
1387   {
1388     GNUNET_break (0);
1389     return;
1390   }
1391   ar = sh->session_array[s];
1392   if (NULL != session)
1393   {
1394     /* FIXME: this is yucky, fix API! */
1395     GNUNET_break (ar->session == session);
1396     ar->session = NULL;
1397     return;
1398   }
1399
1400
1401   GNUNET_assert (NULL != address->transport_name);
1402   namelen = strlen (address->transport_name) + 1;
1403   GNUNET_assert (namelen > 1);
1404   msize = address->address_length + namelen;
1405   if ((msize + sizeof (struct AddressDestroyedMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1406       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1407       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1408   {
1409     GNUNET_break (0);
1410     return;
1411   }
1412
1413   ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1414   m->peer = address->peer;
1415   m->address_length = htons (address->address_length);
1416   m->address_local_info = htonl ((uint32_t) address->local_info);
1417   m->plugin_name_length = htons (namelen);
1418
1419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420               "Deleting address for peer `%s', plugin `%s', session %p\n",
1421               GNUNET_i2s (&address->peer),
1422               address->transport_name,
1423               session);
1424
1425   m->session_id = htonl (s);
1426   pm = (char *) &m[1];
1427   memcpy (pm,
1428           address->address,
1429           address->address_length);
1430   memcpy (&pm[address->address_length],
1431           address->transport_name,
1432           namelen);
1433   GNUNET_MQ_send (sh->mq, ev);
1434   ar->session = NULL;
1435   ar->in_destroy = GNUNET_YES;
1436   GNUNET_array_grow (ar->ats,
1437                      ar->ats_count,
1438                      0);
1439 }
1440
1441
1442 /* end of ats_api_scheduling.c */