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