ed82dd2ada8bb97d4cbc7a0dc860af58eb27e494
[oweals/gnunet.git] / src / ats / ats_api_scheduling.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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  * TODO:
27  * - we could avoid a linear scan over the
28  *   active addresses in some cases, so if
29  *   there is need, we can still optimize here
30  * - we might want to split off the logic to
31  *   determine LAN vs. WAN, as it has nothing
32  *   to do with accessing the ATS service.
33  */
34 #include "platform.h"
35 #include "gnunet_ats_service.h"
36 #include "ats.h"
37
38 /**
39  * How frequently do we scan the interfaces for changes to the addresses?
40  */
41 #define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
42
43 #define LOG(kind,...) GNUNET_log_from(kind, "ats-scheduling-api", __VA_ARGS__)
44
45 /**
46  * Session ID we use if there is no session / slot.
47  */
48 #define NOT_FOUND 0
49
50
51 /**
52  * Information we track per address, incoming or outgoing.  It also
53  * doesn't matter if we have a session, any address that ATS is
54  * allowed to suggest right now should be tracked.
55  */
56 struct GNUNET_ATS_AddressRecord
57 {
58
59   /**
60    * Scheduling handle this address record belongs to.
61    */
62   struct GNUNET_ATS_SchedulingHandle *sh;
63
64   /**
65    * Address data.
66    */
67   struct GNUNET_HELLO_Address *address;
68
69   /**
70    * Session handle.  NULL if we have an address but no
71    * active session for this address.
72    */
73   struct Session *session;
74
75   /**
76    * Array with performance data about the address.
77    */
78   struct GNUNET_ATS_Information *ats;
79
80   /**
81    * Number of entries in @e ats.
82    */
83   uint32_t ats_count;
84
85   /**
86    * Which slot (index) in the session array does
87    * this record correspond to?  FIXME:
88    * FIXME: a linear search on this is really crappy!
89    * Maybe switch to a 64-bit global counter and be
90    * done with it?  Or does that then cause too much
91    * trouble on the ATS-service side?
92    */
93   uint32_t slot;
94
95   /**
96    * We're about to destroy this address record, just ATS does
97    * not know this yet.  Once ATS confirms its destruction,
98    * we can clean up.
99    */
100   int in_destroy;
101 };
102
103
104 /**
105  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
106  */
107 struct GNUNET_ATS_SchedulingHandle
108 {
109
110   /**
111    * Our configuration.
112    */
113   const struct GNUNET_CONFIGURATION_Handle *cfg;
114
115   /**
116    * Callback to invoke on suggestions.
117    */
118   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
119
120   /**
121    * Closure for @e suggest_cb.
122    */
123   void *suggest_cb_cls;
124
125   /**
126    * Connection to ATS service.
127    */
128   struct GNUNET_CLIENT_Connection *client;
129
130   /**
131    * Message queue for sending requests to the ATS service.
132    */
133   struct GNUNET_MQ_Handle *mq;
134
135   /**
136    * Array of session objects (we need to translate them to numbers and back
137    * for the protocol; the offset in the array is the session number on the
138    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
139    * Unused entries are also NULL.
140    */
141   struct GNUNET_ATS_AddressRecord **session_array;
142
143   /**
144    * Task to trigger reconnect.
145    */
146   struct GNUNET_SCHEDULER_Task *task;
147
148   /**
149    * Reconnect backoff delay.
150    */
151   struct GNUNET_TIME_Relative backoff;
152
153   /**
154    * Size of the @e session_array.
155    */
156   unsigned int session_array_size;
157
158 };
159
160
161 /**
162  * Re-establish the connection to the ATS service.
163  *
164  * @param sh handle to use to re-connect.
165  */
166 static void
167 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
168
169
170 /**
171  * Re-establish the connection to the ATS service.
172  *
173  * @param cls handle to use to re-connect.
174  * @param tc scheduler context
175  */
176 static void
177 reconnect_task (void *cls,
178                 const struct GNUNET_SCHEDULER_TaskContext *tc)
179 {
180   struct GNUNET_ATS_SchedulingHandle *sh = cls;
181
182   sh->task = NULL;
183   reconnect (sh);
184 }
185
186
187 /**
188  * Disconnect from ATS and then reconnect.
189  *
190  * @param sh our handle
191  */
192 static void
193 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
194 {
195   if (NULL != sh->mq)
196   {
197     GNUNET_MQ_destroy (sh->mq);
198     sh->mq = NULL;
199   }
200   if (NULL != sh->client)
201   {
202     GNUNET_CLIENT_disconnect (sh->client);
203     sh->client = NULL;
204   }
205   sh->suggest_cb (sh->suggest_cb_cls,
206                   NULL, NULL, NULL,
207                   GNUNET_BANDWIDTH_ZERO,
208                   GNUNET_BANDWIDTH_ZERO);
209   sh->backoff = GNUNET_TIME_STD_BACKOFF (sh->backoff);
210   sh->task = GNUNET_SCHEDULER_add_delayed (sh->backoff,
211                                            &reconnect_task,
212                                            sh);
213 }
214
215
216 /**
217  * Find the session object corresponding to the given session ID.
218  *
219  * @param sh our handle
220  * @param session_id current session ID
221  * @param peer peer the session belongs to
222  * @return the session object (or NULL)
223  */
224 static struct GNUNET_ATS_AddressRecord *
225 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
226               uint32_t session_id,
227               const struct GNUNET_PeerIdentity *peer)
228 {
229   struct GNUNET_ATS_AddressRecord *ar;
230
231   if (session_id >= sh->session_array_size)
232   {
233     GNUNET_break (0);
234     return NULL;
235   }
236   if (0 == session_id)
237     return NULL;
238   ar = sh->session_array[session_id];
239   if (NULL == ar)
240   {
241     GNUNET_break (0);
242     return NULL;
243   }
244   if (NULL == ar->address)
245   {
246     /* address was destroyed in the meantime, this can happen
247        as we communicate asynchronously with the ATS service. */
248     return NULL;
249   }
250   if (0 != memcmp (peer,
251                    &ar->address->peer,
252                    sizeof (struct GNUNET_PeerIdentity)))
253   {
254     GNUNET_break (0);
255     force_reconnect (sh);
256     return NULL;
257   }
258   return ar;
259 }
260
261
262 /**
263  * Get an available session ID.
264  *
265  * @param sh our handle
266  * @return an unused slot, but never NOT_FOUND (0)
267  */
268 static uint32_t
269 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh)
270 {
271   static uint32_t off;
272   uint32_t i;
273
274   i = 0;
275   while ( ( (NOT_FOUND == off) ||
276             (NULL != sh->session_array[off % sh->session_array_size]) ) &&
277           (i < sh->session_array_size) )
278   {
279     off++;
280     i++;
281   }
282   if ( (NOT_FOUND != off % sh->session_array_size) &&
283        (NULL == sh->session_array[off % sh->session_array_size]) )
284     return off;
285   i = sh->session_array_size;
286   GNUNET_array_grow (sh->session_array,
287                      sh->session_array_size,
288                      sh->session_array_size * 2);
289   return i;
290 }
291
292
293 /**
294  * Get the ID for the given session object.
295  *
296  * @param sh our handle
297  * @param session session object
298  * @param address the address we are looking for
299  * @return the session id or NOT_FOUND for error
300  */
301 static uint32_t
302 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
303                  struct Session *session,
304                  const struct GNUNET_HELLO_Address *address)
305 {
306   uint32_t i;
307
308   if (NULL == address)
309   {
310     GNUNET_break (0);
311     return NOT_FOUND;
312   }
313   for (i = 1; i < sh->session_array_size; i++)
314     if ( (NULL != sh->session_array[i]) &&
315          ( (session == sh->session_array[i]->session) ||
316            (NULL == sh->session_array[i]->session) ) &&
317          (0 == GNUNET_HELLO_address_cmp (address,
318                                          sh->session_array[i]->address)) )
319       return i;
320   return NOT_FOUND;
321 }
322
323
324 /**
325  * Release the session slot from the session table (ATS service is
326  * also done using it).
327  *
328  * @param sh our handle
329  * @param session_id identifies session that is no longer valid
330  */
331 static void
332 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
333                  uint32_t session_id)
334 {
335   struct GNUNET_ATS_AddressRecord *ar;
336
337   if (NOT_FOUND == session_id)
338     return;
339   if (session_id >= sh->session_array_size)
340   {
341     GNUNET_break (0);
342     force_reconnect (sh);
343     return;
344   }
345   /* this slot should have been removed from remove_session before */
346   ar = sh->session_array[session_id];
347   if (NULL != ar->session)
348   {
349     GNUNET_break (0);
350     force_reconnect (sh);
351     return;
352   }
353   GNUNET_HELLO_address_free (ar->address);
354   GNUNET_free (ar);
355   sh->session_array[session_id] = NULL;
356 }
357
358
359 /**
360  * Type of a function to call when we receive a session release
361  * message from the service.
362  *
363  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
364  * @param msg message received, NULL on timeout or fatal error
365  */
366 static void
367 process_ats_session_release_message (void *cls,
368                                      const struct GNUNET_MessageHeader *msg)
369 {
370   struct GNUNET_ATS_SchedulingHandle *sh = cls;
371   const struct SessionReleaseMessage *srm;
372
373   srm = (const struct SessionReleaseMessage *) msg;
374   /* Note: peer field in srm not necessary right now,
375      but might be good to have in the future */
376   release_session (sh,
377                    ntohl (srm->session_id));
378 }
379
380
381 /**
382  * Type of a function to call when we receive a address suggestion
383  * message from the service.
384  *
385  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
386  * @param msg message received, NULL on timeout or fatal error
387  */
388 static void
389 process_ats_address_suggestion_message (void *cls,
390                                         const struct GNUNET_MessageHeader *msg)
391 {
392   struct GNUNET_ATS_SchedulingHandle *sh = cls;
393   const struct AddressSuggestionMessage *m;
394   struct GNUNET_ATS_AddressRecord *ar;
395   uint32_t session_id;
396
397   m = (const struct AddressSuggestionMessage *) msg;
398   session_id = ntohl (m->session_id);
399   if (0 == session_id)
400   {
401     GNUNET_break (0);
402     force_reconnect (sh);
403     return;
404   }
405   ar = find_session (sh, session_id, &m->peer);
406   if (NULL == ar)
407   {
408     GNUNET_break (0);
409     force_reconnect (sh);
410     return;
411   }
412   if (NULL == sh->suggest_cb)
413     return;
414   if (GNUNET_YES == ar->in_destroy)
415   {
416     /* ignore suggestion, as this address is dying, unless BW is 0,
417        in that case signal 'disconnect' via BW 0 */
418     if ( (0 == ntohl (m->bandwidth_out.value__)) &&
419          (0 == ntohl (m->bandwidth_in.value__)) )
420     {
421       LOG (GNUNET_ERROR_TYPE_DEBUG,
422            "ATS suggests disconnect from peer `%s' with BW %u/%u\n",
423            GNUNET_i2s (&ar->address->peer),
424            (unsigned int) ntohl (m->bandwidth_out.value__),
425            (unsigned int) ntohl (m->bandwidth_in.value__));
426       sh->suggest_cb (sh->suggest_cb_cls,
427                       &m->peer,
428                       NULL,
429                       NULL,
430                       m->bandwidth_out,
431                       m->bandwidth_in);
432     }
433     return;
434   }
435   if ( (NULL == ar->session) &&
436        (GNUNET_HELLO_address_check_option (ar->address,
437                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND)) )
438   {
439     GNUNET_break (0);
440     return;
441   }
442   sh->backoff = GNUNET_TIME_UNIT_ZERO;
443   LOG (GNUNET_ERROR_TYPE_DEBUG,
444        "ATS suggests address slot %u for peer `%s' using plugin %s\n",
445        ar->slot,
446        GNUNET_i2s (&ar->address->peer),
447        ar->address->transport_name);
448   sh->suggest_cb (sh->suggest_cb_cls,
449                   &m->peer,
450                   ar->address,
451                   ar->session,
452                   m->bandwidth_out,
453                   m->bandwidth_in);
454 }
455
456
457 /**
458  * We encountered an error handling the MQ to the
459  * ATS service.  Reconnect.
460  *
461  * @param cls the `struct GNUNET_ATS_SchedulingHandle`
462  * @param error details about the error
463  */
464 static void
465 error_handler (void *cls,
466                enum GNUNET_MQ_Error error)
467 {
468   struct GNUNET_ATS_SchedulingHandle *sh = cls;
469
470   LOG (GNUNET_ERROR_TYPE_WARNING,
471        "ATS connection died (code %d), reconnecting\n",
472        (int) error);
473   force_reconnect (sh);
474 }
475
476
477 /**
478  * Generate and transmit the `struct AddressAddMessage` for the given
479  * address record.
480  *
481  * @param sh the scheduling handle to use for transmission
482  * @param ar the address to inform the ATS service about
483  */
484 static void
485 send_add_address_message (struct GNUNET_ATS_SchedulingHandle *sh,
486                           const struct GNUNET_ATS_AddressRecord *ar)
487 {
488   struct GNUNET_MQ_Envelope *ev;
489   struct AddressAddMessage *m;
490   struct GNUNET_ATS_Information *am;
491   char *pm;
492   size_t namelen;
493   size_t msize;
494
495   if (NULL == sh->mq)
496     return; /* disconnected, skip for now */
497   namelen = (NULL == ar->address->transport_name)
498     ? 0
499     : strlen (ar->address->transport_name) + 1;
500   msize = ar->address->address_length +
501     ar->ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
502
503   ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
504   m->ats_count = htonl (ar->ats_count);
505   m->peer = ar->address->peer;
506   m->address_length = htons (ar->address->address_length);
507   m->address_local_info = htonl ((uint32_t) ar->address->local_info);
508   m->plugin_name_length = htons (namelen);
509   m->session_id = htonl (ar->slot);
510
511   LOG (GNUNET_ERROR_TYPE_DEBUG,
512        "Adding address for peer `%s', plugin `%s', session %p slot %u\n",
513        GNUNET_i2s (&ar->address->peer),
514        ar->address->transport_name,
515        ar->session,
516        ar->slot);
517   am = (struct GNUNET_ATS_Information *) &m[1];
518   memcpy (am,
519           ar->ats,
520           ar->ats_count * sizeof (struct GNUNET_ATS_Information));
521   pm = (char *) &am[ar->ats_count];
522   memcpy (pm,
523           ar->address->address,
524           ar->address->address_length);
525   if (NULL != ar->address->transport_name)
526     memcpy (&pm[ar->address->address_length],
527             ar->address->transport_name,
528             namelen);
529   GNUNET_MQ_send (sh->mq, ev);
530 }
531
532
533 /**
534  * Re-establish the connection to the ATS service.
535  *
536  * @param sh handle to use to re-connect.
537  */
538 static void
539 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
540 {
541   static const struct GNUNET_MQ_MessageHandler handlers[] =
542     { { &process_ats_session_release_message,
543         GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
544         sizeof (struct SessionReleaseMessage) },
545       { &process_ats_address_suggestion_message,
546         GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
547         sizeof (struct AddressSuggestionMessage) },
548       { NULL, 0, 0 } };
549   struct GNUNET_MQ_Envelope *ev;
550   struct ClientStartMessage *init;
551   unsigned int i;
552   struct GNUNET_ATS_AddressRecord *ar;
553
554   GNUNET_assert (NULL == sh->client);
555   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
556   if (NULL == sh->client)
557   {
558     force_reconnect (sh);
559     return;
560   }
561   sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
562                                                   handlers,
563                                                   &error_handler,
564                                                   sh);
565   ev = GNUNET_MQ_msg (init,
566                       GNUNET_MESSAGE_TYPE_ATS_START);
567   init->start_flag = htonl (START_FLAG_SCHEDULING);
568   GNUNET_MQ_send (sh->mq, ev);
569   if (NULL == sh->mq)
570     return;
571   for (i=0;i<sh->session_array_size;i++)
572   {
573     ar = sh->session_array[i];
574     if (NULL == ar)
575       continue;
576     send_add_address_message (sh, ar);
577     if (NULL == sh->mq)
578       return;
579   }
580 }
581
582
583 /**
584  * Initialize the ATS subsystem.
585  *
586  * @param cfg configuration to use
587  * @param suggest_cb notification to call whenever the suggestation changed
588  * @param suggest_cb_cls closure for @a suggest_cb
589  * @return ats context
590  */
591 struct GNUNET_ATS_SchedulingHandle *
592 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
593                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
594                             void *suggest_cb_cls)
595 {
596   struct GNUNET_ATS_SchedulingHandle *sh;
597
598   sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
599   sh->cfg = cfg;
600   sh->suggest_cb = suggest_cb;
601   sh->suggest_cb_cls = suggest_cb_cls;
602   GNUNET_array_grow (sh->session_array,
603                      sh->session_array_size,
604                      4);
605   reconnect (sh);
606   return sh;
607 }
608
609
610 /**
611  * Client is done with ATS scheduling, release resources.
612  *
613  * @param sh handle to release
614  */
615 void
616 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
617 {
618   unsigned int i;
619
620   if (NULL != sh->mq)
621   {
622     GNUNET_MQ_destroy (sh->mq);
623     sh->mq = NULL;
624   }
625   if (NULL != sh->client)
626   {
627     GNUNET_CLIENT_disconnect (sh->client);
628     sh->client = NULL;
629   }
630   if (NULL != sh->task)
631   {
632     GNUNET_SCHEDULER_cancel (sh->task);
633     sh->task = NULL;
634   }
635   for (i=0;i<sh->session_array_size;i++)
636   {
637     GNUNET_free_non_null (sh->session_array[i]);
638     sh->session_array[i] = NULL;
639   }
640   GNUNET_array_grow (sh->session_array,
641                      sh->session_array_size,
642                      0);
643   GNUNET_free (sh);
644 }
645
646
647 /**
648  * Test if a address and a session is known to ATS
649  *
650  * @param sh the scheduling handle
651  * @param address the address
652  * @param session the session
653  * @return #GNUNET_YES or #GNUNET_NO
654  */
655 int
656 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
657                           const struct GNUNET_HELLO_Address *address,
658                           struct Session *session)
659 {
660   if (NULL == session)
661     return GNUNET_NO;
662   if (NOT_FOUND != find_session_id (sh,
663                                     session,
664                                     address))
665     return GNUNET_YES;  /* Exists */
666   return GNUNET_NO;
667 }
668
669
670 /**
671  * We have a new address ATS should know. Addresses have to be added
672  * with this function before they can be: updated, set in use and
673  * destroyed.
674  *
675  * @param sh handle
676  * @param address the address
677  * @param session session handle, can be NULL
678  * @param ats performance data for the address
679  * @param ats_count number of performance records in @a ats
680  * @return handle to the address representation inside ATS, NULL
681  *         on error (i.e. ATS knows this exact address already)
682  */
683 struct GNUNET_ATS_AddressRecord *
684 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
685                         const struct GNUNET_HELLO_Address *address,
686                         struct Session *session,
687                         const struct GNUNET_ATS_Information *ats,
688                         uint32_t ats_count)
689 {
690   struct GNUNET_ATS_AddressRecord *ar;
691   size_t namelen;
692   size_t msize;
693   uint32_t s;
694
695   if (NULL == address)
696   {
697     /* we need a valid address */
698     GNUNET_break (0);
699     return NULL;
700   }
701   namelen = (NULL == address->transport_name)
702     ? 0
703     : strlen (address->transport_name) + 1;
704   msize = address->address_length +
705     ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
706   if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
707       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
708       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
709       (ats_count >=
710        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
711   {
712     /* address too large for us, this should not happen */
713     GNUNET_break (0);
714     return NULL;
715   }
716
717   if (NOT_FOUND !=
718       find_session_id (sh,
719                        session,
720                        address))
721   {
722     /* Already existing, nothing todo, but this should not happen */
723     GNUNET_break (0);
724     return NULL;
725   }
726   s = find_empty_session_slot (sh);
727   ar = GNUNET_new (struct GNUNET_ATS_AddressRecord);
728   ar->sh = sh;
729   ar->slot = s;
730   ar->session = session;
731   ar->address = GNUNET_HELLO_address_copy (address);
732   GNUNET_array_grow (ar->ats,
733                      ar->ats_count,
734                      ats_count);
735   memcpy (ar->ats,
736           ats,
737           ats_count * sizeof (struct GNUNET_ATS_Information));
738   sh->session_array[s] = ar;
739   send_add_address_message (sh, ar);
740   return ar;
741 }
742
743
744 /**
745  * An address was used to initiate a session.
746  *
747  * @param ar address record to update information for
748  * @param session session handle
749  */
750 void
751 GNUNET_ATS_address_add_session (struct GNUNET_ATS_AddressRecord *ar,
752                                 struct Session *session)
753 {
754   GNUNET_break (NULL == ar->session);
755   ar->session = session;
756 }
757
758
759 /**
760  * A session was destroyed, disassociate it from the
761  * given address record.  If this was an incoming
762  * addess, destroy the address as well.
763  *
764  * @param ar address record to update information for
765  * @param session session handle
766  * @return #GNUNET_YES if the @a ar was destroyed because
767  *                     it was an incoming address,
768  *         #GNUNET_NO if the @ar was kept because we can
769  *                    use it still to establish a new session
770  */
771 int
772 GNUNET_ATS_address_del_session (struct GNUNET_ATS_AddressRecord *ar,
773                                 struct Session *session)
774 {
775   GNUNET_break (session == ar->session);
776   ar->session = NULL;
777   if (GNUNET_HELLO_address_check_option (ar->address,
778                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
779   {
780     GNUNET_ATS_address_destroy (ar);
781     return GNUNET_YES;
782   }
783   return GNUNET_NO;
784 }
785
786
787 /**
788  * We have updated performance statistics for a given address.  Note
789  * that this function can be called for addresses that are currently
790  * in use as well as addresses that are valid but not actively in use.
791  * Furthermore, the peer may not even be connected to us right now (in
792  * which case the call may be ignored or the information may be stored
793  * for later use).  Update bandwidth assignments.
794  *
795  * @param ar address record to update information for
796  * @param ats performance data for the address
797  * @param ats_count number of performance records in @a ats
798  */
799 void
800 GNUNET_ATS_address_update (struct GNUNET_ATS_AddressRecord *ar,
801                            const struct GNUNET_ATS_Information *ats,
802                            uint32_t ats_count)
803 {
804   struct GNUNET_ATS_SchedulingHandle *sh = ar->sh;
805   struct GNUNET_MQ_Envelope *ev;
806   struct AddressUpdateMessage *m;
807   struct GNUNET_ATS_Information *am;
808   size_t msize;
809
810   LOG (GNUNET_ERROR_TYPE_DEBUG,
811        "Updating address for peer `%s', plugin `%s', session %p slot %u\n",
812        GNUNET_i2s (&ar->address->peer),
813        ar->address->transport_name,
814        ar->session,
815        ar->slot);
816   GNUNET_array_grow (ar->ats,
817                      ar->ats_count,
818                      ats_count);
819   memcpy (ar->ats,
820           ats,
821           ats_count * sizeof (struct GNUNET_ATS_Information));
822
823   if (NULL == sh->mq)
824     return; /* disconnected, skip for now */
825   msize = ar->ats_count * sizeof (struct GNUNET_ATS_Information);
826   ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
827   m->ats_count = htonl (ar->ats_count);
828   m->peer = ar->address->peer;
829   m->session_id = htonl (ar->slot);
830   am = (struct GNUNET_ATS_Information *) &m[1];
831   memcpy (am,
832           ar->ats,
833           ar->ats_count * sizeof (struct GNUNET_ATS_Information));
834   GNUNET_MQ_send (sh->mq, ev);
835 }
836
837
838
839 /**
840  * An address got destroyed, stop using it as a valid address.
841  *
842  * @param ar address to destroy
843  */
844 void
845 GNUNET_ATS_address_destroy (struct GNUNET_ATS_AddressRecord *ar)
846 {
847   struct GNUNET_ATS_SchedulingHandle *sh = ar->sh;
848   struct GNUNET_MQ_Envelope *ev;
849   struct AddressDestroyedMessage *m;
850
851   LOG (GNUNET_ERROR_TYPE_DEBUG,
852        "Deleting address for peer `%s', plugin `%s', slot %u session %p\n",
853        GNUNET_i2s (&ar->address->peer),
854        ar->address->transport_name,
855        ar->slot,
856        ar->session);
857   GNUNET_break (NULL == ar->session);
858   ar->session = NULL;
859   ar->in_destroy = GNUNET_YES;
860   GNUNET_array_grow (ar->ats,
861                      ar->ats_count,
862                      0);
863   if (NULL == sh->mq)
864     return;
865   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
866   m->session_id = htonl (ar->slot);
867   m->peer = ar->address->peer;
868   GNUNET_MQ_send (sh->mq, ev);
869 }
870
871
872 /* end of ats_api_scheduling.c */