d2bf06c179a1d6e9fddacc4fa5c8c7c6f4cedf28
[oweals/gnunet.git] / src / ats / ats_api_scheduling.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "ats.h"
29
30
31 #define INTERFACE_PROCESSING_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
32
33 #define NOT_FOUND 0
34
35 /**
36  * Message in linked list we should send to the ATS service.  The
37  * actual binary message follows this struct.
38  */
39 struct PendingMessage
40 {
41
42   /**
43    * Kept in a DLL.
44    */
45   struct PendingMessage *next;
46
47   /**
48    * Kept in a DLL.
49    */
50   struct PendingMessage *prev;
51
52   /**
53    * Size of the message.
54    */
55   size_t size;
56
57   /**
58    * Is this the 'ATS_START' message?
59    */
60   int is_init;
61 };
62
63
64 /**
65  * Information we track per session.
66  */
67 struct SessionRecord
68 {
69   /**
70    * Identity of the peer (just needed for error checking).
71    */
72   struct GNUNET_PeerIdentity peer;
73
74   /**
75    * Session handle.
76    */
77   struct Session *session;
78
79   /**
80    * Set to #GNUNET_YES if the slot is used.
81    */
82   int slot_used;
83 };
84
85
86 struct ATS_Network
87 {
88   struct ATS_Network * next;
89
90   struct ATS_Network * prev;
91
92   struct sockaddr *network;
93
94   struct sockaddr *netmask;
95
96   socklen_t length;
97 };
98
99
100 /**
101  * Handle for address suggestions
102  */
103 struct GNUNET_ATS_SuggestHandle
104 {
105   struct GNUNET_ATS_SuggestHandle *prev;
106
107   struct GNUNET_ATS_SuggestHandle *next;
108
109   struct GNUNET_PeerIdentity id;
110 };
111
112
113 /**
114  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
115  */
116 struct GNUNET_ATS_SchedulingHandle
117 {
118
119   /**
120    * Our configuration.
121    */
122   const struct GNUNET_CONFIGURATION_Handle *cfg;
123
124   /**
125    * Callback to invoke on suggestions.
126    */
127   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
128
129   /**
130    * Closure for @e suggest_cb.
131    */
132   void *suggest_cb_cls;
133
134   /**
135    * DLL for suggestions head
136    */
137   struct GNUNET_ATS_SuggestHandle *sug_head;
138
139   /**
140    * DLL for suggestions tail
141    */
142   struct GNUNET_ATS_SuggestHandle *sug_tail;
143
144   /**
145    * Connection to ATS service.
146    */
147   struct GNUNET_CLIENT_Connection *client;
148
149   /**
150    * Head of list of messages for the ATS service.
151    */
152   struct PendingMessage *pending_head;
153
154   /**
155    * Tail of list of messages for the ATS service
156    */
157   struct PendingMessage *pending_tail;
158
159   /**
160    * Current request for transmission to ATS.
161    */
162   struct GNUNET_CLIENT_TransmitHandle *th;
163
164   /**
165    * Head of network list
166    */
167   struct ATS_Network * net_head;
168
169   /**
170    * Tail of network list
171    */
172   struct ATS_Network * net_tail;
173
174   /**
175    * Array of session objects (we need to translate them to numbers and back
176    * for the protocol; the offset in the array is the session number on the
177    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
178    * Unused entries are also NULL.
179    */
180   struct SessionRecord *session_array;
181
182   /**
183    * Task to trigger reconnect.
184    */
185   struct GNUNET_SCHEDULER_Task * task;
186
187   /**
188    * Task retrieving interfaces from the system
189    */
190   struct GNUNET_SCHEDULER_Task * interface_task;
191
192
193   /**
194    * Size of the session array.
195    */
196   unsigned int session_array_size;
197
198   /**
199    * Should we reconnect to ATS due to some serious error?
200    */
201   int reconnect;
202 };
203
204
205 /**
206  * Re-establish the connection to the ATS service.
207  *
208  * @param sh handle to use to re-connect.
209  */
210 static void
211 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
212
213
214 /**
215  * Re-establish the connection to the ATS service.
216  *
217  * @param cls handle to use to re-connect.
218  * @param tc scheduler context
219  */
220 static void
221 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
222 {
223   struct GNUNET_ATS_SchedulingHandle *sh = cls;
224
225   sh->task = NULL;
226   reconnect (sh);
227 }
228
229
230 /**
231  * Disconnect from ATS and then reconnect.
232  *
233  * @param sh our handle
234  */
235 static void
236 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
237 {
238   sh->reconnect = GNUNET_NO;
239   GNUNET_CLIENT_disconnect (sh->client);
240   sh->client = NULL;
241   sh->task =
242       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
243                                     sh);
244 }
245
246
247 /**
248  * Transmit messages from the message queue to the service
249  * (if there are any, and if we are not already trying).
250  *
251  * @param sh handle to use
252  */
253 static void
254 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh);
255
256
257 /**
258  * Type of a function to call when we receive a message
259  * from the service.
260  *
261  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
262  * @param msg message received, NULL on timeout or fatal error
263  */
264 static void
265 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg);
266
267
268 /**
269  * We can now transmit a message to ATS. Do it.
270  *
271  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
272  * @param size number of bytes we can transmit to ATS
273  * @param buf where to copy the messages
274  * @return number of bytes copied into buf
275  */
276 static size_t
277 transmit_message_to_ats (void *cls, size_t size, void *buf)
278 {
279   struct GNUNET_ATS_SchedulingHandle *sh = cls;
280   struct PendingMessage *p;
281   size_t ret;
282   char *cbuf;
283
284   sh->th = NULL;
285   if ((size == 0) || (buf == NULL))
286   {
287     force_reconnect (sh);
288     return 0;
289   }
290   ret = 0;
291   cbuf = buf;
292   while ((NULL != (p = sh->pending_head)) && (p->size <= size))
293   {
294     memcpy (&cbuf[ret], &p[1], p->size);
295     ret += p->size;
296     size -= p->size;
297     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
298     GNUNET_free (p);
299   }
300   do_transmit (sh);
301   return ret;
302 }
303
304
305 /**
306  * Transmit messages from the message queue to the service
307  * (if there are any, and if we are not already trying).
308  *
309  * @param sh handle to use
310  */
311 static void
312 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh)
313 {
314   struct PendingMessage *p;
315
316   if (NULL != sh->th)
317     return;
318   if (NULL == (p = sh->pending_head))
319     return;
320   if (NULL == sh->client)
321     return;                     /* currently reconnecting */
322   sh->th =
323       GNUNET_CLIENT_notify_transmit_ready (sh->client, p->size,
324                                            GNUNET_TIME_UNIT_FOREVER_REL,
325                                            GNUNET_NO, &transmit_message_to_ats,
326                                            sh);
327 }
328
329
330 /**
331  * Find the session object corresponding to the given session ID.
332  *
333  * @param sh our handle
334  * @param session_id current session ID
335  * @param peer peer the session belongs to
336  * @return the session object (or NULL)
337  */
338 static struct Session *
339 find_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
340               const struct GNUNET_PeerIdentity *peer)
341 {
342
343   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
344               "Find session %u from peer %s in %p\n",
345               (unsigned int) session_id, GNUNET_i2s (peer), sh);
346
347   if (session_id >= sh->session_array_size)
348   {
349     GNUNET_break (0);
350     return NULL;
351   }
352   if (0 == session_id)
353     return NULL;
354   if (sh->session_array[session_id].session == NULL)
355   {
356     GNUNET_break (0 ==
357                   memcmp (peer, &sh->session_array[session_id].peer,
358                           sizeof (struct GNUNET_PeerIdentity)));
359     return NULL;
360   }
361
362   if (0 !=
363       memcmp (peer, &sh->session_array[session_id].peer,
364               sizeof (struct GNUNET_PeerIdentity)))
365   {
366     GNUNET_break (0);
367     sh->reconnect = GNUNET_YES;
368     return NULL;
369   }
370   /* This check exploits the fact that first field of a session object
371    * is peer identity.
372    */
373   if (0 !=
374       memcmp (peer, sh->session_array[session_id].session,
375               sizeof (struct GNUNET_PeerIdentity)))
376   {
377     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
378               "Session %p belongs to peer `%s'\n",
379               sh->session_array[session_id].session, GNUNET_i2s_full ((struct GNUNET_PeerIdentity *) &sh->session_array[session_id].peer));
380 /*
381     GNUNET_break (0);
382     sh->reconnect = GNUNET_YES;
383     return NULL;
384 */
385   }
386   return sh->session_array[session_id].session;
387 }
388
389
390 /**
391  * Get an available session ID for the given session object.
392  *
393  * @param sh our handle
394  * @param session session object
395  * @param peer peer the session belongs to
396  * @return the session id
397  */
398 static uint32_t
399 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
400                 const struct GNUNET_PeerIdentity *peer)
401 {
402   unsigned int i;
403   unsigned int f;
404
405   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
406               "Get session ID for session %p from peer %s in %p\n", session,
407               GNUNET_i2s (peer), sh);
408
409   if (NULL == session)
410     return NOT_FOUND;
411   f = 0;
412   for (i = 1; i < sh->session_array_size; i++)
413   {
414     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
415       f = i;
416   }
417   if (f == 0)
418   {
419     f = sh->session_array_size;
420     GNUNET_array_grow (sh->session_array, sh->session_array_size,
421                        sh->session_array_size * 2);
422   }
423   GNUNET_assert (f > 0);
424   sh->session_array[f].session = session;
425   sh->session_array[f].peer = *peer;
426   sh->session_array[f].slot_used = GNUNET_YES;
427
428   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
429               "Assigning session ID %u for session %p of peer %s in %p\n", f,
430               session, GNUNET_i2s (peer), sh);
431
432   return f;
433 }
434
435
436 /**
437  * Get the ID for the given session object.
438  *
439  * @param sh our handle
440  * @param session session object
441  * @param peer peer the session belongs to
442  * @return the session id or NOT_FOUND for error
443  */
444 static uint32_t
445 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
446                 const struct GNUNET_PeerIdentity *peer)
447 {
448   unsigned int i;
449   char * p2;
450
451   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
452               "Get session ID for session %p from peer %s in %p\n", session,
453               GNUNET_i2s (peer), sh);
454
455   if (NULL == session)
456     return NOT_FOUND;
457   for (i = 1; i < sh->session_array_size; i++)
458   {
459     if (session == sh->session_array[i].session)
460     {
461       if (0 != memcmp (peer, &sh->session_array[i].peer,
462                        sizeof (struct GNUNET_PeerIdentity)))
463       {
464         p2 = strdup (GNUNET_i2s (&sh->session_array[i].peer));
465         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "ats-scheduling-api",
466                     "Session %p did not match: old session was for peer `%s' new session is for `%s'\n",
467                     session, GNUNET_i2s (peer), p2);
468         GNUNET_free (p2);
469         return NOT_FOUND;
470       }
471       return i;
472     }
473   }
474   return NOT_FOUND;
475 }
476
477
478 /**
479  * Remove the session of the given session ID from the session
480  * table (it is no longer valid).
481  *
482  * @param sh our handle
483  * @param session_id identifies session that is no longer valid
484  * @param peer peer the session belongs to
485  */
486 static void
487 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
488                 const struct GNUNET_PeerIdentity *peer)
489 {
490   GNUNET_assert (peer != NULL);
491   GNUNET_assert (sh != NULL);
492
493   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
494               "Release sessionID %u from peer %s in %p\n",
495               (unsigned int) session_id, GNUNET_i2s (peer), sh);
496
497   if (0 == session_id)
498     return;
499
500   GNUNET_assert (session_id < sh->session_array_size);
501   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
502   GNUNET_assert (0 == memcmp (peer,
503                               &sh->session_array[session_id].peer,
504                               sizeof (struct GNUNET_PeerIdentity)));
505   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
506               "Session %p for peer `%s' removed from slot %u \n",
507               sh->session_array[session_id].session,
508               GNUNET_i2s (peer),
509               session_id);
510   sh->session_array[session_id].session = NULL;
511
512 }
513
514
515 /**
516  * Release the session slot from the session table (ATS service is
517  * also done using it).
518  *
519  * @param sh our handle
520  * @param session_id identifies session that is no longer valid
521  * @param peer peer the session belongs to
522  */
523 static void
524 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
525                  const struct GNUNET_PeerIdentity *peer)
526 {
527
528   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
529               "Release sessionID %u from peer %s in %p\n",
530               (unsigned int) session_id, GNUNET_i2s (peer), sh);
531
532   if (session_id >= sh->session_array_size)
533   {
534     GNUNET_break (0);
535     sh->reconnect = GNUNET_YES;
536     return;
537   }
538
539   /* this slot should have been removed from remove_session before */
540   GNUNET_assert (sh->session_array[session_id].session == NULL);
541
542   if (0 !=
543       memcmp (peer, &sh->session_array[session_id].peer,
544               sizeof (struct GNUNET_PeerIdentity)))
545   {
546     GNUNET_break (0);
547     sh->reconnect = GNUNET_YES;
548     return;
549   }
550   sh->session_array[session_id].slot_used = GNUNET_NO;
551   memset (&sh->session_array[session_id].peer, 0,
552           sizeof (struct GNUNET_PeerIdentity));
553 }
554
555
556 static void
557 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
558                          const struct SessionReleaseMessage *srm)
559 {
560   release_session (sh, ntohl (srm->session_id), &srm->peer);
561 }
562
563
564 /**
565  * Type of a function to call when we receive a message
566  * from the service.
567  *
568  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
569  * @param msg message received, NULL on timeout or fatal error
570  */
571 static void
572 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
573 {
574   struct GNUNET_ATS_SchedulingHandle *sh = cls;
575   const struct AddressSuggestionMessage *m;
576   const struct GNUNET_ATS_Information *atsi;
577   const char *plugin_address;
578   const char *plugin_name;
579   uint16_t plugin_address_length;
580   uint16_t plugin_name_length;
581   uint32_t ats_count;
582   struct GNUNET_HELLO_Address address;
583   struct Session *s;
584
585   if (NULL == msg)
586   {
587     force_reconnect (sh);
588     return;
589   }
590   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
591       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
592   {
593     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
594     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
595                            GNUNET_TIME_UNIT_FOREVER_REL);
596     if (GNUNET_YES == sh->reconnect)
597       force_reconnect (sh);
598     return;
599   }
600   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
601       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
602   {
603     GNUNET_break (0);
604     force_reconnect (sh);
605     return;
606   }
607   m = (const struct AddressSuggestionMessage *) msg;
608   ats_count = ntohl (m->ats_count);
609   plugin_address_length = ntohs (m->address_length);
610   atsi = (const struct GNUNET_ATS_Information *) &m[1];
611   plugin_address = (const char *) &atsi[ats_count];
612   plugin_name = &plugin_address[plugin_address_length];
613   plugin_name_length = ntohs (m->plugin_name_length);
614   if ((plugin_address_length + plugin_name_length +
615        ats_count * sizeof (struct GNUNET_ATS_Information) +
616        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
617       (ats_count >
618        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
619       || (plugin_name[plugin_name_length - 1] != '\0'))
620   {
621     GNUNET_break (0);
622     force_reconnect (sh);
623     return;
624   }
625   uint32_t session_id = ntohl (m->session_id);
626
627   if (session_id == 0)
628     s = NULL;
629   else
630   {
631     s = find_session (sh, session_id, &m->peer);
632     if (s == NULL)
633     {
634
635       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
636                   "ATS tries to use outdated session `%s'\n",
637                   GNUNET_i2s (&m->peer));
638       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
639                              GNUNET_TIME_UNIT_FOREVER_REL);
640       return;
641     }
642   }
643
644   if (NULL == sh->suggest_cb)
645         return;
646
647   address.peer = m->peer;
648   address.address = plugin_address;
649   address.address_length = plugin_address_length;
650   address.transport_name = plugin_name;
651   address.local_info = ntohl(m->address_local_info);
652
653   if ((s == NULL) && (0 == address.address_length))
654   {
655     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
656                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
657                 GNUNET_i2s (&address.peer), address.transport_name,
658                 plugin_address_length, session_id);
659     GNUNET_break_op (0);
660     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
661                            GNUNET_TIME_UNIT_FOREVER_REL);
662     return;
663   }
664
665   sh->suggest_cb (sh->suggest_cb_cls,
666                   (const struct GNUNET_PeerIdentity *) &m->peer,
667                   &address, s, m->bandwidth_out,
668                   m->bandwidth_in, atsi, ats_count);
669
670   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
671                          GNUNET_TIME_UNIT_FOREVER_REL);
672   if (GNUNET_YES == sh->reconnect)
673     force_reconnect (sh);
674 }
675
676
677 /**
678  * Re-establish the connection to the ATS service.
679  *
680  * @param sh handle to use to re-connect.
681  */
682 static void
683 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
684 {
685   struct PendingMessage *p;
686   struct ClientStartMessage *init;
687
688   GNUNET_assert (NULL == sh->client);
689   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
690   GNUNET_assert (NULL != sh->client);
691   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
692                            GNUNET_TIME_UNIT_FOREVER_REL);
693   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
694   {
695     p = GNUNET_malloc (sizeof (struct PendingMessage) +
696                        sizeof (struct ClientStartMessage));
697     p->size = sizeof (struct ClientStartMessage);
698     p->is_init = GNUNET_YES;
699     init = (struct ClientStartMessage *) &p[1];
700     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
701     init->header.size = htons (sizeof (struct ClientStartMessage));
702     init->start_flag = htonl (START_FLAG_SCHEDULING);
703     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
704   }
705   do_transmit (sh);
706 }
707
708
709 /**
710  * delete the current network list
711  */
712 static void
713 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
714 {
715   struct ATS_Network * cur = sh->net_head;
716   while (cur != NULL)
717   {
718     GNUNET_CONTAINER_DLL_remove(sh->net_head, sh->net_tail, cur);
719     GNUNET_free (cur);
720     cur = sh->net_head;
721   }
722 }
723
724
725 static int
726 interface_proc (void *cls, const char *name,
727                 int isDefault,
728                 const struct sockaddr *
729                 addr,
730                 const struct sockaddr *
731                 broadcast_addr,
732                 const struct sockaddr *
733                 netmask, socklen_t addrlen)
734 {
735   struct GNUNET_ATS_SchedulingHandle * sh = cls;
736   /* Calculate network */
737   struct ATS_Network *net = NULL;
738
739   /* Skipping IPv4 loopback addresses since we have special check  */
740   if  (addr->sa_family == AF_INET)
741   {
742     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
743
744     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
745        return GNUNET_OK;
746   }
747   /* Skipping IPv6 loopback addresses since we have special check  */
748   if  (addr->sa_family == AF_INET6)
749   {
750     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
751     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
752       return GNUNET_OK;
753   }
754
755   if (addr->sa_family == AF_INET)
756   {
757     struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
758     struct sockaddr_in *netmask4 = (struct sockaddr_in *) netmask;
759     struct sockaddr_in *tmp = NULL;
760     struct sockaddr_in network4;
761
762     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
763     tmp = (struct sockaddr_in *) &net[1];
764     net->network = (struct sockaddr *) &tmp[0];
765     net->netmask = (struct sockaddr *) &tmp[1];
766     net->length = addrlen;
767
768     memset (&network4, 0, sizeof (network4));
769     network4.sin_family = AF_INET;
770 #if HAVE_SOCKADDR_IN_SIN_LEN
771     network4.sin_len = sizeof (network4);
772 #endif
773     network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
774
775     memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
776     memcpy (net->network, &network4, sizeof (struct sockaddr_in));
777   }
778
779   if (addr->sa_family == AF_INET6)
780   {
781     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
782     struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) netmask;
783     struct sockaddr_in6 * tmp = NULL;
784     struct sockaddr_in6 network6;
785
786     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
787     tmp = (struct sockaddr_in6 *) &net[1];
788     net->network = (struct sockaddr *) &tmp[0];
789     net->netmask = (struct sockaddr *) &tmp[1];
790     net->length = addrlen;
791
792     memset (&network6, 0, sizeof (network6));
793     network6.sin6_family = AF_INET6;
794 #if HAVE_SOCKADDR_IN_SIN_LEN
795     network6.sin6_len = sizeof (network6);
796 #endif
797     int c = 0;
798     uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
799     uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
800     uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
801     for (c = 0; c < 4; c++)
802       net_elem[c] = addr_elem[c] & mask_elem[c];
803
804     memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
805     memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
806   }
807
808   /* Store in list */
809   if (net != NULL)
810   {
811 #if VERBOSE_ATS
812     char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
813     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding network `%s', netmask `%s'\n",
814         GNUNET_a2s((struct sockaddr *) net->network, addrlen),
815         netmask);
816     GNUNET_free (netmask);
817 # endif
818     GNUNET_CONTAINER_DLL_insert(sh->net_head, sh->net_tail, net);
819   }
820   return GNUNET_OK;
821 }
822
823
824 /**
825  * Periodically get list of addresses
826  * @param cls closure
827  * @param tc Task context
828  */
829 static void
830 get_addresses (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
831 {
832   struct GNUNET_ATS_SchedulingHandle * sh = cls;
833   sh->interface_task = NULL;
834   delete_networks (sh);
835   GNUNET_OS_network_interfaces_list(interface_proc, sh);
836   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
837                                                      get_addresses,
838                                                      sh);
839 }
840
841
842 /**
843  * Convert a `enum GNUNET_ATS_Network_Type` to a string
844  *
845  * @param net the network type
846  * @return a string or NULL if invalid
847  */
848 const char *
849 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
850 {
851   switch (net)
852     {
853     case GNUNET_ATS_NET_UNSPECIFIED:
854       return "UNSPECIFIED";
855     case GNUNET_ATS_NET_LOOPBACK:
856       return "LOOPBACK";
857     case GNUNET_ATS_NET_LAN:
858       return "LAN";
859     case GNUNET_ATS_NET_WAN:
860       return "WAN";
861     case GNUNET_ATS_NET_WLAN:
862       return "WLAN";
863     case GNUNET_ATS_NET_BT:
864       return "BLUETOOTH";
865     default:
866       return NULL;
867     }
868 }
869
870
871 /**
872  * Convert a ATS property to a string
873  *
874  * @param type the property type
875  * @return a string or NULL if invalid
876  */
877 const char *
878 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
879 {
880   switch (type)
881   {
882   case GNUNET_ATS_ARRAY_TERMINATOR:
883     return "TERMINATOR";
884   case GNUNET_ATS_UTILIZATION_OUT:
885     return "UTILIZATION_UP";
886   case GNUNET_ATS_UTILIZATION_IN:
887     return "UTILIZATION_DOWN";
888   case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
889     return "UTILIZATION_PAYLOAD_UP";
890   case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
891     return "UTILIZATION_PAYLOAD_DOWN";
892   case GNUNET_ATS_NETWORK_TYPE:
893     return "NETWORK_TYPE";
894   case GNUNET_ATS_QUALITY_NET_DELAY:
895     return "DELAY";
896   case GNUNET_ATS_QUALITY_NET_DISTANCE:
897     return "DISTANCE";
898   case GNUNET_ATS_COST_WAN:
899     return "COST_WAN";
900   case GNUNET_ATS_COST_LAN:
901     return "COST_LAN";
902   case GNUNET_ATS_COST_WLAN:
903     return "COST_WLAN";
904   default:
905     return NULL;
906   }
907 }
908
909
910 /**
911  * Returns where the address is located: LAN or WAN or ...
912  *
913  * @param sh the scheduling handle
914  * @param addr address
915  * @param addrlen address length
916  * @return location as GNUNET_ATS_Information
917  */
918 struct GNUNET_ATS_Information
919 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
920 {
921   GNUNET_assert (sh != NULL);
922   struct ATS_Network * cur = sh->net_head;
923
924   int type = GNUNET_ATS_NET_UNSPECIFIED;
925   struct GNUNET_ATS_Information ats;
926
927   if  (addr->sa_family == AF_UNIX)
928   {
929     type = GNUNET_ATS_NET_LOOPBACK;
930   }
931
932   /* IPv4 loopback check */
933   if  (addr->sa_family == AF_INET)
934   {
935     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
936
937     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
938       type = GNUNET_ATS_NET_LOOPBACK;
939   }
940   /* IPv6 loopback check */
941   if  (addr->sa_family == AF_INET6)
942   {
943     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
944     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
945       type = GNUNET_ATS_NET_LOOPBACK;
946   }
947
948   /* Check local networks */
949   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
950   {
951     if (addrlen != cur->length)
952     {
953       cur = cur->next;
954       continue;
955     }
956
957     if (addr->sa_family == AF_INET)
958     {
959       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
960       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
961       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
962
963       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
964         type = GNUNET_ATS_NET_LAN;
965     }
966     if (addr->sa_family == AF_INET6)
967     {
968       struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
969       struct sockaddr_in6 * net6 = (struct sockaddr_in6 *) cur->network;
970       struct sockaddr_in6 * mask6 = (struct sockaddr_in6 *) cur->netmask;
971
972       int res = GNUNET_YES;
973       int c = 0;
974       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
975       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
976       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
977       for (c = 0; c < 4; c++)
978         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
979           res = GNUNET_NO;
980
981       if (res == GNUNET_YES)
982         type = GNUNET_ATS_NET_LAN;
983     }
984     cur = cur->next;
985   }
986
987   /* no local network found for this address, default: WAN */
988   if (type == GNUNET_ATS_NET_UNSPECIFIED)
989     type = GNUNET_ATS_NET_WAN;
990   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
991   ats.value = htonl (type);
992
993   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
994                    "`%s' is in network `%s'\n",
995                    GNUNET_a2s ((const struct sockaddr *) addr, addrlen),
996                    GNUNET_ATS_print_network_type(type));
997   return ats;
998 }
999
1000
1001 /**
1002  * Initialize the ATS subsystem.
1003  *
1004  * @param cfg configuration to use
1005  * @param suggest_cb notification to call whenever the suggestation changed
1006  * @param suggest_cb_cls closure for 'suggest_cb'
1007  * @return ats context
1008  */
1009 struct GNUNET_ATS_SchedulingHandle *
1010 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1011                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
1012                             void *suggest_cb_cls)
1013 {
1014   struct GNUNET_ATS_SchedulingHandle *sh;
1015
1016   sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
1017   sh->cfg = cfg;
1018   sh->suggest_cb = suggest_cb;
1019   sh->suggest_cb_cls = suggest_cb_cls;
1020   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
1021   GNUNET_OS_network_interfaces_list(interface_proc, sh);
1022   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
1023       get_addresses,
1024       sh);
1025   reconnect (sh);
1026   return sh;
1027 }
1028
1029
1030 /**
1031  * Client is done with ATS scheduling, release resources.
1032  *
1033  * @param sh handle to release
1034  */
1035 void
1036 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1037 {
1038   struct PendingMessage *p;
1039   struct GNUNET_ATS_SuggestHandle *cur;
1040   struct GNUNET_ATS_SuggestHandle *next;
1041   while (NULL != (p = sh->pending_head))
1042   {
1043     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
1044     GNUNET_free (p);
1045   }
1046   if (NULL != sh->client)
1047   {
1048     GNUNET_CLIENT_disconnect (sh->client);
1049     sh->client = NULL;
1050   }
1051   if (NULL != sh->task)
1052   {
1053     GNUNET_SCHEDULER_cancel (sh->task);
1054     sh->task = NULL;
1055   }
1056
1057   next = sh->sug_head;
1058   while (NULL != (cur = next))
1059   {
1060                 next = cur->next;
1061                 GNUNET_CONTAINER_DLL_remove (sh->sug_head, sh->sug_tail, cur);
1062                 GNUNET_free (cur);
1063   }
1064
1065   delete_networks (sh);
1066   if (sh->interface_task != NULL)
1067   {
1068     GNUNET_SCHEDULER_cancel(sh->interface_task);
1069     sh->interface_task = NULL;
1070   }
1071   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
1072   GNUNET_free (sh);
1073   sh = NULL;
1074 }
1075
1076 /**
1077  * We would like to reset the address suggestion block time for this
1078  * peer
1079  *
1080  * @param sh handle
1081  * @param peer identity of the peer we want to reset
1082  */
1083 void
1084 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1085                           const struct GNUNET_PeerIdentity *peer)
1086 {
1087   struct PendingMessage *p;
1088   struct ResetBackoffMessage *m;
1089
1090   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1091                      sizeof (struct ResetBackoffMessage));
1092   p->size = sizeof (struct ResetBackoffMessage);
1093   p->is_init = GNUNET_NO;
1094   m = (struct ResetBackoffMessage *) &p[1];
1095   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1096   m->header.size = htons (sizeof (struct ResetBackoffMessage));
1097   m->reserved = htonl (0);
1098   m->peer = *peer;
1099   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1100   do_transmit (sh);
1101 }
1102
1103
1104 /**
1105  * We would like to receive address suggestions for a peer. ATS will
1106  * respond with a call to the continuation immediately containing an address or
1107  * no address if none is available. ATS can suggest more addresses until we call
1108  * #GNUNET_ATS_suggest_address_cancel().
1109  *
1110  * @param sh handle
1111  * @param peer identity of the peer we need an address for
1112  * @return suggest handle
1113  */
1114 struct GNUNET_ATS_SuggestHandle *
1115 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1116                             const struct GNUNET_PeerIdentity *peer)
1117 {
1118   struct PendingMessage *p;
1119   struct RequestAddressMessage *m;
1120   struct GNUNET_ATS_SuggestHandle *s;
1121
1122   // FIXME: ATS needs to remember this in case of
1123   // a disconnect!
1124   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1125                      sizeof (struct RequestAddressMessage));
1126   p->size = sizeof (struct RequestAddressMessage);
1127   m = (struct RequestAddressMessage *) &p[1];
1128   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1129   m->header.size = htons (sizeof (struct RequestAddressMessage));
1130   m->reserved = htonl (0);
1131   m->peer = *peer;
1132   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head,
1133                                     sh->pending_tail,
1134                                     p);
1135   do_transmit (sh);
1136   s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1137   s->id = *peer;
1138   GNUNET_CONTAINER_DLL_insert_tail (sh->sug_head,
1139                                     sh->sug_tail,
1140                                     s);
1141   return s;
1142 }
1143
1144
1145 /**
1146  * We would like to stop receiving address updates for this peer
1147  *
1148  * @param sh handle
1149  * @param peer identity of the peer
1150  */
1151 void
1152 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1153                                    const struct GNUNET_PeerIdentity *peer)
1154 {
1155   struct PendingMessage *p;
1156   struct RequestAddressMessage *m;
1157   struct GNUNET_ATS_SuggestHandle *s;
1158
1159   for (s = sh->sug_head; NULL != s; s = s->next)
1160         if (0 == memcmp(peer, &s->id, sizeof (s->id)))
1161                 break;
1162   if (NULL == s)
1163   {
1164         GNUNET_break (0);
1165         return;
1166   }
1167   else
1168   {
1169         GNUNET_CONTAINER_DLL_remove (sh->sug_head, sh->sug_tail, s);
1170         GNUNET_free (s);
1171   }
1172
1173   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1174                      sizeof (struct RequestAddressMessage));
1175   p->size = sizeof (struct RequestAddressMessage);
1176   p->is_init = GNUNET_NO;
1177   m = (struct RequestAddressMessage *) &p[1];
1178   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1179   m->header.size = htons (sizeof (struct RequestAddressMessage));
1180   m->reserved = htonl (0);
1181   m->peer = *peer;
1182   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1183   do_transmit (sh);
1184 }
1185
1186
1187 /**
1188  * Test if a address and a session is known to ATS
1189  *
1190  * @param sh the scheduling handle
1191  * @param address the address
1192  * @param session the session
1193  * @return GNUNET_YES or GNUNET_NO
1194  */
1195 int
1196 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1197                                                                                         const struct GNUNET_HELLO_Address *address,
1198                                                                                         struct Session *session)
1199 {
1200         int s;
1201   if (NULL != session)
1202   {
1203     if (NOT_FOUND != (s = find_session_id (sh, session, &address->peer)))
1204     {
1205       /* Existing */
1206       return GNUNET_YES;
1207     }
1208     return GNUNET_NO;
1209   }
1210   return GNUNET_NO;
1211 }
1212
1213 /**
1214  * We have a new address ATS should know. Addresses have to be added with this
1215  * function before they can be: updated, set in use and destroyed
1216  *
1217  * @param sh handle
1218  * @param address the address
1219  * @param session session handle, can be NULL
1220  * @param ats performance data for the address
1221  * @param ats_count number of performance records in 'ats'
1222  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1223  */
1224 int
1225 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1226                         const struct GNUNET_HELLO_Address *address,
1227                         struct Session *session,
1228                         const struct GNUNET_ATS_Information *ats,
1229                         uint32_t ats_count)
1230 {
1231
1232   struct PendingMessage *p;
1233   struct AddressUpdateMessage *m;
1234   struct GNUNET_ATS_Information *am;
1235   char *pm;
1236   size_t namelen;
1237   size_t msize;
1238   uint32_t s = 0;
1239
1240   if (address == NULL)
1241   {
1242     GNUNET_break (0);
1243     return GNUNET_SYSERR;
1244   }
1245
1246   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
1247
1248   msize = sizeof (struct AddressUpdateMessage) + address->address_length +
1249       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1250   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1251       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1252       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1253       (ats_count >=
1254        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1255   {
1256     GNUNET_break (0);
1257     return GNUNET_SYSERR;
1258   }
1259
1260   if (NULL != session)
1261   {
1262     if (NOT_FOUND != (s = find_session_id (sh, session, &address->peer)))
1263     {
1264       /* Already existing, nothing todo */
1265       return GNUNET_SYSERR;
1266     }
1267     s = find_empty_session_slot (sh, session, &address->peer);
1268     GNUNET_break (NOT_FOUND != s);
1269   }
1270
1271   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1272   p->size = msize;
1273   p->is_init = GNUNET_NO;
1274   m = (struct AddressUpdateMessage *) &p[1];
1275   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1276   m->header.size = htons (msize);
1277   m->ats_count = htonl (ats_count);
1278   m->peer = address->peer;
1279   m->address_length = htons (address->address_length);
1280   m->address_local_info = htonl ((uint32_t) address->local_info);
1281   m->plugin_name_length = htons (namelen);
1282   m->session_id = htonl (s);
1283
1284   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1285               "Adding address for peer `%s', plugin `%s', session %p id %u\n",
1286               GNUNET_i2s (&address->peer),
1287               address->transport_name, session, s);
1288
1289   am = (struct GNUNET_ATS_Information *) &m[1];
1290   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1291   pm = (char *) &am[ats_count];
1292   memcpy (pm, address->address, address->address_length);
1293   if (NULL != address->transport_name)
1294         memcpy (&pm[address->address_length], address->transport_name, namelen);
1295   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1296   do_transmit (sh);
1297   return GNUNET_OK;
1298
1299 }
1300
1301
1302 /**
1303  * We have updated performance statistics for a given address.  Note
1304  * that this function can be called for addresses that are currently
1305  * in use as well as addresses that are valid but not actively in use.
1306  * Furthermore, the peer may not even be connected to us right now (in
1307  * which case the call may be ignored or the information may be stored
1308  * for later use).  Update bandwidth assignments.
1309  *
1310  * @param sh handle
1311  * @param address the address
1312  * @param session session handle, can be NULL
1313  * @param ats performance data for the address
1314  * @param ats_count number of performance records in @a ats
1315  * @return #GNUNET_YES on success, #GNUNET_NO if address or session are unknown,
1316  * #GNUNET_SYSERR on hard failure
1317  */
1318 int
1319 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1320                            const struct GNUNET_HELLO_Address *address,
1321                            struct Session *session,
1322                            const struct GNUNET_ATS_Information *ats,
1323                            uint32_t ats_count)
1324 {
1325   struct PendingMessage *p;
1326   struct AddressUpdateMessage *m;
1327   struct GNUNET_ATS_Information *am;
1328   char *pm;
1329   size_t namelen;
1330   size_t msize;
1331   uint32_t s = 0;
1332
1333   if (NULL == address)
1334   {
1335     GNUNET_break (0);
1336     return GNUNET_SYSERR;
1337   }
1338   if (NULL == sh)
1339   {
1340     GNUNET_break (0);
1341     return GNUNET_SYSERR;
1342   }
1343
1344   namelen = (address->transport_name ==
1345        NULL) ? 0 : strlen (address->transport_name) + 1;
1346   msize =
1347       sizeof (struct AddressUpdateMessage) + address->address_length +
1348       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1349   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1350       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1351       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1352       (ats_count >=
1353        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1354   {
1355     GNUNET_break (0);
1356     return GNUNET_SYSERR;
1357   }
1358
1359   if (NULL != session)
1360   {
1361     s = find_session_id (sh, session, &address->peer);
1362     if (NOT_FOUND == s)
1363       return GNUNET_NO;
1364   }
1365
1366   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1367   p->size = msize;
1368   p->is_init = GNUNET_NO;
1369   m = (struct AddressUpdateMessage *) &p[1];
1370   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1371   m->header.size = htons (msize);
1372   m->ats_count = htonl (ats_count);
1373   m->peer = address->peer;
1374   m->address_length = htons (address->address_length);
1375   m->address_local_info = htonl ((uint32_t) address->local_info);
1376   m->plugin_name_length = htons (namelen);
1377
1378   m->session_id = htonl (s);
1379
1380   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1381               "Updating address for peer `%s', plugin `%s', session %p id %u\n",
1382               GNUNET_i2s (&address->peer),
1383               address->transport_name, session, s);
1384
1385   am = (struct GNUNET_ATS_Information *) &m[1];
1386   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1387   pm = (char *) &am[ats_count];
1388   memcpy (pm, address->address, address->address_length);
1389   memcpy (&pm[address->address_length], address->transport_name, namelen);
1390   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1391   do_transmit (sh);
1392   return GNUNET_YES;
1393 }
1394
1395
1396 /**
1397  * An address is now in use or not used any more.
1398  *
1399  * @param sh handle
1400  * @param address the address
1401  * @param session session handle, can be NULL
1402  * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1403  * if address is not used any more
1404  */
1405 void
1406 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1407                            const struct GNUNET_HELLO_Address *address,
1408                            struct Session *session, int in_use)
1409 {
1410   struct PendingMessage *p;
1411   struct AddressUseMessage *m;
1412   char *pm;
1413   size_t namelen;
1414   size_t msize;
1415   uint32_t s = 0;
1416
1417   GNUNET_assert (NULL != address);
1418   namelen =
1419       (address->transport_name ==
1420        NULL) ? 0 : strlen (address->transport_name) + 1;
1421   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
1422   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1423       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1424       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1425   {
1426     GNUNET_break (0);
1427     return;
1428   }
1429
1430   if (session != NULL)
1431   {
1432     s = find_session_id (sh, session, &address->peer);
1433     if ((s == NOT_FOUND) && (GNUNET_NO == in_use))
1434     {
1435       /* trying to set unknown address to NO */
1436       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1437                   "Trying to set unknown address to unused for peer `%s', plugin `%s', session %p\n",
1438                   GNUNET_i2s (&address->peer), address->transport_name, session);
1439       GNUNET_break (0);
1440       return;
1441     }
1442     if ((s == NOT_FOUND) && (GNUNET_YES == in_use))
1443     {
1444       /* trying to set new address to YES */
1445       s = find_empty_session_slot (sh, session, &address->peer);
1446       GNUNET_assert (NOT_FOUND != s);
1447     }
1448   }
1449
1450   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1451   p->size = msize;
1452   p->is_init = GNUNET_NO;
1453   m = (struct AddressUseMessage *) &p[1];
1454   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1455   m->header.size = htons (msize);
1456   m->peer = address->peer;
1457   m->in_use = htons (in_use);
1458   m->address_length = htons (address->address_length);
1459   m->address_local_info = htonl ((uint32_t) address->local_info);
1460   m->plugin_name_length = htons (namelen);
1461
1462   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1463               "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1464               (GNUNET_YES == in_use) ? "YES" : "NO",
1465               GNUNET_i2s (&address->peer), address->transport_name, session);
1466
1467   m->session_id = htonl (s);
1468   pm = (char *) &m[1];
1469   memcpy (pm, address->address, address->address_length);
1470   memcpy (&pm[address->address_length], address->transport_name, namelen);
1471   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1472   do_transmit (sh);
1473   return;
1474 }
1475
1476
1477 /**
1478  * An address got destroyed, stop including it as a valid address.
1479  *
1480  * If a session is given, only the session will be removed, if no session is
1481  * given the full address will be deleted.
1482  *
1483  * @param sh handle
1484  * @param address the address
1485  * @param session session handle that is no longer valid, can be NULL
1486  */
1487 void
1488 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1489                               const struct GNUNET_HELLO_Address *address,
1490                               struct Session *session)
1491 {
1492   struct PendingMessage *p;
1493   struct AddressDestroyedMessage *m;
1494   char *pm;
1495   size_t namelen;
1496   size_t msize;
1497   uint32_t s = 0;
1498
1499   if (address == NULL)
1500   {
1501     GNUNET_break (0);
1502     return;
1503   }
1504
1505   GNUNET_assert (address->transport_name != NULL);
1506   namelen = strlen (address->transport_name) + 1;
1507   GNUNET_assert (namelen > 1);
1508   msize =
1509       sizeof (struct AddressDestroyedMessage) + address->address_length +
1510       namelen;
1511   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1512       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1513       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1514   {
1515     GNUNET_break (0);
1516     return;
1517   }
1518
1519   s = find_session_id (sh, session, &address->peer);
1520   if ((NULL != session) && (NOT_FOUND == s))
1521   {
1522     /* trying to delete unknown address */
1523     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1524                 "Trying to delete unknown address for peer `%s', plugin `%s', session %p\n",
1525                 GNUNET_i2s (&address->peer), address->transport_name, session);
1526     return;
1527   }
1528
1529   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1530   p->size = msize;
1531   p->is_init = GNUNET_NO;
1532   m = (struct AddressDestroyedMessage *) &p[1];
1533   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1534   m->header.size = htons (msize);
1535   m->reserved = htonl (0);
1536   m->peer = address->peer;
1537   m->address_length = htons (address->address_length);
1538   m->address_local_info = htonl ((uint32_t) address->local_info);
1539   m->plugin_name_length = htons (namelen);
1540
1541   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1542               "Deleting address for peer `%s', plugin `%s', session %p\n",
1543               GNUNET_i2s (&address->peer), address->transport_name, session);
1544
1545   m->session_id = htonl (s);
1546   pm = (char *) &m[1];
1547   memcpy (pm, address->address, address->address_length);
1548   memcpy (&pm[address->address_length], address->transport_name, namelen);
1549   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1550   do_transmit (sh);
1551   remove_session (sh, s, &address->peer);
1552 }
1553
1554 /* end of ats_api_scheduling.c */