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