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