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