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