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