- log from ats
[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 #define DEBUG_ATS GNUNET_EXTRA_LOGGING
31
32 #define INTERFACE_PROCESSING_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
33
34 /**
35  * Message in linked list we should send to the ATS service.  The
36  * actual binary message follows this struct.
37  */
38 struct PendingMessage
39 {
40
41   /**
42    * Kept in a DLL.
43    */
44   struct PendingMessage *next;
45
46   /**
47    * Kept in a DLL.
48    */
49   struct PendingMessage *prev;
50
51   /**
52    * Size of the message.
53    */
54   size_t size;
55
56   /**
57    * Is this the 'ATS_START' message?
58    */
59   int is_init;
60 };
61
62
63 /**
64  * Information we track per session.
65  */
66 struct SessionRecord
67 {
68   /**
69    * Identity of the peer (just needed for error checking).
70    */
71   struct GNUNET_PeerIdentity peer;
72
73   /**
74    * Session handle.
75    */
76   struct Session *session;
77
78   /**
79    * Set to GNUNET_YES if the slot is used.
80    */
81   int slot_used;
82 };
83
84
85 struct ATS_Network
86 {
87   struct ATS_Network * next;
88
89   struct ATS_Network * prev;
90
91   struct sockaddr *network;
92   struct sockaddr *netmask;
93   socklen_t length;
94 };
95
96
97
98 /**
99  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
100  */
101 struct GNUNET_ATS_SchedulingHandle
102 {
103
104   /**
105    * Our configuration.
106    */
107   const struct GNUNET_CONFIGURATION_Handle *cfg;
108
109   /**
110    * Callback to invoke on suggestions.
111    */
112   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
113
114   /**
115    * Closure for 'suggest_cb'.
116    */
117   void *suggest_cb_cls;
118
119   /**
120    * Connection to ATS service.
121    */
122   struct GNUNET_CLIENT_Connection *client;
123
124   /**
125    * Head of list of messages for the ATS service.
126    */
127   struct PendingMessage *pending_head;
128
129   /**
130    * Tail of list of messages for the ATS service
131    */
132   struct PendingMessage *pending_tail;
133
134   /**
135    * Current request for transmission to ATS.
136    */
137   struct GNUNET_CLIENT_TransmitHandle *th;
138
139   /**
140    * Head of network list
141    */
142   struct ATS_Network * net_head;
143
144   /**
145    * Tail of network list
146    */
147   struct ATS_Network * net_tail;
148
149
150   /**
151    * Array of session objects (we need to translate them to numbers and back
152    * for the protocol; the offset in the array is the session number on the
153    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
154    * Unused entries are also NULL.
155    */
156   struct SessionRecord *session_array;
157
158   /**
159    * Task to trigger reconnect.
160    */
161   GNUNET_SCHEDULER_TaskIdentifier task;
162
163   /**
164    * Task retrieving interfaces from the system
165    */
166
167   GNUNET_SCHEDULER_TaskIdentifier interface_task;
168
169
170   /**
171    * Size of the session array.
172    */
173   unsigned int session_array_size;
174
175   /**
176    * Should we reconnect to ATS due to some serious error?
177    */
178   int reconnect;
179 };
180
181
182 /**
183  * Re-establish the connection to the ATS service.
184  *
185  * @param sh handle to use to re-connect.
186  */
187 static void
188 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
189
190
191 /**
192  * Re-establish the connection to the ATS service.
193  *
194  * @param cls handle to use to re-connect.
195  * @param tc scheduler context
196  */
197 static void
198 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
199 {
200   struct GNUNET_ATS_SchedulingHandle *sh = cls;
201
202   sh->task = GNUNET_SCHEDULER_NO_TASK;
203   reconnect (sh);
204 }
205
206
207 /**
208  * Disconnect from ATS and then reconnect.
209  *
210  * @param sh our handle
211  */
212 static void
213 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
214 {
215   sh->reconnect = GNUNET_NO;
216   GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
217   sh->client = NULL;
218   sh->task =
219       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
220                                     sh);
221 }
222
223
224 /**
225  * Transmit messages from the message queue to the service
226  * (if there are any, and if we are not already trying).
227  *
228  * @param sh handle to use
229  */
230 static void
231 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh);
232
233
234 /**
235  * Type of a function to call when we receive a message
236  * from the service.
237  *
238  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
239  * @param msg message received, NULL on timeout or fatal error
240  */
241 static void
242 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg);
243
244
245 /**
246  * We can now transmit a message to ATS. Do it.
247  *
248  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
249  * @param size number of bytes we can transmit to ATS
250  * @param buf where to copy the messages
251  * @return number of bytes copied into buf
252  */
253 static size_t
254 transmit_message_to_ats (void *cls, size_t size, void *buf)
255 {
256   struct GNUNET_ATS_SchedulingHandle *sh = cls;
257   struct PendingMessage *p;
258   size_t ret;
259   char *cbuf;
260
261   sh->th = NULL;
262   if ((size == 0) || (buf == NULL))
263   {
264     force_reconnect (sh);
265     return 0;
266   }
267   ret = 0;
268   cbuf = buf;
269   while ((NULL != (p = sh->pending_head)) && (p->size <= size))
270   {
271     memcpy (&cbuf[ret], &p[1], p->size);
272     ret += p->size;
273     size -= p->size;
274     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
275     if (GNUNET_YES == p->is_init)
276       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
277                              GNUNET_TIME_UNIT_FOREVER_REL);
278     GNUNET_free (p);
279   }
280   do_transmit (sh);
281   return ret;
282 }
283
284
285 /**
286  * Transmit messages from the message queue to the service
287  * (if there are any, and if we are not already trying).
288  *
289  * @param sh handle to use
290  */
291 static void
292 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh)
293 {
294   struct PendingMessage *p;
295
296   if (NULL != sh->th)
297     return;
298   if (NULL == (p = sh->pending_head))
299     return;
300   if (NULL == sh->client)
301     return;                     /* currently reconnecting */
302   sh->th =
303       GNUNET_CLIENT_notify_transmit_ready (sh->client, p->size,
304                                            GNUNET_TIME_UNIT_FOREVER_REL,
305                                            GNUNET_NO, &transmit_message_to_ats,
306                                            sh);
307 }
308
309
310 /**
311  * Find the session object corresponding to the given session ID.
312  *
313  * @param sh our handle
314  * @param session_id current session ID
315  * @param peer peer the session belongs to
316  * @return the session object (or NULL)
317  */
318 static struct Session *
319 find_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
320               const struct GNUNET_PeerIdentity *peer)
321 {
322
323   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
324               "Find session %u from peer %s in %p\n",
325               (unsigned int) session_id, GNUNET_i2s (peer), sh);
326
327   if (session_id >= sh->session_array_size)
328   {
329     GNUNET_break (0);
330     return NULL;
331   }
332   if (0 == session_id)
333     return NULL;
334   if (sh->session_array[session_id].session == NULL)
335   {
336     GNUNET_break (0 ==
337                   memcmp (peer, &sh->session_array[session_id].peer,
338                           sizeof (struct GNUNET_PeerIdentity)));
339     return NULL;
340   }
341
342   if (0 !=
343       memcmp (peer, &sh->session_array[session_id].peer,
344               sizeof (struct GNUNET_PeerIdentity)))
345   {
346     GNUNET_break (0);
347     sh->reconnect = GNUNET_YES;
348     return NULL;
349   }
350   return sh->session_array[session_id].session;
351 }
352
353
354 /**
355  * Get the ID for the given session object.  If we do not have an ID for
356  * the given session object, allocate one.
357  *
358  * @param sh our handle
359  * @param session session object
360  * @param peer peer the session belongs to
361  * @return the session id
362  */
363 static uint32_t
364 get_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
365                 const struct GNUNET_PeerIdentity *peer)
366 {
367   unsigned int i;
368   unsigned int f;
369
370
371   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
372               "Get session ID for session %p from peer %s in %p\n", session,
373               GNUNET_i2s (peer), sh);
374
375   if (NULL == session)
376     return 0;
377   f = 0;
378   for (i = 1; i < sh->session_array_size; i++)
379   {
380     if (session == sh->session_array[i].session)
381     {
382       GNUNET_assert (0 ==
383                      memcmp (peer, &sh->session_array[i].peer,
384                              sizeof (struct GNUNET_PeerIdentity)));
385       return i;
386     }
387     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
388       f = i;
389   }
390   if (f == 0)
391   {
392     f = sh->session_array_size;
393     GNUNET_array_grow (sh->session_array, sh->session_array_size,
394                        sh->session_array_size * 2);
395   }
396   GNUNET_assert (f > 0);
397   sh->session_array[f].session = session;
398   sh->session_array[f].peer = *peer;
399   sh->session_array[f].slot_used = GNUNET_YES;
400
401   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
402               "Assigning session ID %u for session %p of peer %s in %p\n", f,
403               session, GNUNET_i2s (peer), sh);
404
405   return f;
406 }
407
408
409 /**
410  * Remove the session of the given session ID from the session
411  * table (it is no longer valid).
412  *
413  * @param sh our handle
414  * @param session_id identifies session that is no longer valid
415  * @param peer peer the session belongs to
416  */
417 static void
418 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
419                 const struct GNUNET_PeerIdentity *peer)
420 {
421   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
422               "Remove sessionID %u from peer %s in %p\n", "ats-scheduling-api",
423               (unsigned int) session_id, GNUNET_i2s (peer), sh);
424   if (0 == session_id)
425     return;
426   GNUNET_assert (session_id < sh->session_array_size);
427   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
428   GNUNET_assert (0 ==
429                  memcmp (peer, &sh->session_array[session_id].peer,
430                          sizeof (struct GNUNET_PeerIdentity)));
431   sh->session_array[session_id].session = NULL;
432 }
433
434
435 /**
436  * Release the session slot from the session table (ATS service is
437  * also done using it).
438  *
439  * @param sh our handle
440  * @param session_id identifies session that is no longer valid
441  * @param peer peer the session belongs to
442  */
443 static void
444 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
445                  const struct GNUNET_PeerIdentity *peer)
446 {
447
448   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
449               "Release sessionID %u from peer %s in %p\n",
450               (unsigned int) session_id, GNUNET_i2s (peer), sh);
451
452   if (session_id >= sh->session_array_size)
453   {
454     GNUNET_break (0);
455     sh->reconnect = GNUNET_YES;
456     return;
457   }
458
459   /* this slot should have been removed from remove_session before */
460   GNUNET_assert (sh->session_array[session_id].session == NULL);
461
462   if (0 !=
463       memcmp (peer, &sh->session_array[session_id].peer,
464               sizeof (struct GNUNET_PeerIdentity)))
465   {
466     GNUNET_break (0);
467     sh->reconnect = GNUNET_YES;
468     return;
469   }
470   sh->session_array[session_id].slot_used = GNUNET_NO;
471   memset (&sh->session_array[session_id].peer, 0,
472           sizeof (struct GNUNET_PeerIdentity));
473 }
474
475
476 static void
477 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
478                          const struct SessionReleaseMessage *srm)
479 {
480   release_session (sh, ntohl (srm->session_id), &srm->peer);
481 }
482
483
484 /**
485  * Type of a function to call when we receive a message
486  * from the service.
487  *
488  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
489  * @param msg message received, NULL on timeout or fatal error
490  */
491 static void
492 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
493 {
494   struct GNUNET_ATS_SchedulingHandle *sh = cls;
495   const struct AddressSuggestionMessage *m;
496   const struct GNUNET_ATS_Information *atsi;
497   const char *plugin_address;
498   const char *plugin_name;
499   uint16_t plugin_address_length;
500   uint16_t plugin_name_length;
501   uint32_t ats_count;
502   struct GNUNET_HELLO_Address address;
503   struct Session *s;
504
505   if (NULL == msg)
506   {
507     force_reconnect (sh);
508     return;
509   }
510   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
511       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
512   {
513     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
514     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
515                            GNUNET_TIME_UNIT_FOREVER_REL);
516     if (GNUNET_YES == sh->reconnect)
517       force_reconnect (sh);
518     return;
519   }
520   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
521       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
522   {
523     GNUNET_break (0);
524     force_reconnect (sh);
525     return;
526   }
527   m = (const struct AddressSuggestionMessage *) msg;
528   ats_count = ntohl (m->ats_count);
529   plugin_address_length = ntohs (m->address_length);
530   atsi = (const struct GNUNET_ATS_Information *) &m[1];
531   plugin_address = (const char *) &atsi[ats_count];
532   plugin_name = &plugin_address[plugin_address_length];
533   plugin_name_length = ntohs (m->plugin_name_length);
534   if ((plugin_address_length + plugin_name_length +
535        ats_count * sizeof (struct GNUNET_ATS_Information) +
536        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
537       (ats_count >
538        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
539       || (plugin_name[plugin_name_length - 1] != '\0'))
540   {
541     GNUNET_break (0);
542     force_reconnect (sh);
543     return;
544   }
545   uint32_t session_id = ntohl (m->session_id);
546
547   if (session_id == 0)
548     s = NULL;
549   else
550   {
551     s = find_session (sh, session_id, &m->peer);
552     if (s == NULL)
553     {
554
555       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
556                   "ATS tries to use outdated session `%s'\n",
557                   GNUNET_i2s (&m->peer));
558       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
559                              GNUNET_TIME_UNIT_FOREVER_REL);
560       return;
561     }
562   }
563   address.peer = m->peer;
564   address.address = plugin_address;
565   address.address_length = plugin_address_length;
566   address.transport_name = plugin_name;
567
568   if ((s == NULL) && (0 == address.address_length))
569   {
570     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
571                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
572                 GNUNET_i2s (&address.peer), address.transport_name,
573                 plugin_address_length, session_id);
574     GNUNET_break_op (0);
575     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
576                            GNUNET_TIME_UNIT_FOREVER_REL);
577     return;
578   }
579
580   sh->suggest_cb (sh->suggest_cb_cls, &address, s, m->bandwidth_out,
581                   m->bandwidth_in, atsi, ats_count);
582
583   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
584                          GNUNET_TIME_UNIT_FOREVER_REL);
585   if (GNUNET_YES == sh->reconnect)
586     force_reconnect (sh);
587 }
588
589
590 /**
591  * Re-establish the connection to the ATS service.
592  *
593  * @param sh handle to use to re-connect.
594  */
595 static void
596 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
597 {
598   struct PendingMessage *p;
599   struct ClientStartMessage *init;
600
601   GNUNET_assert (NULL == sh->client);
602   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
603   GNUNET_assert (NULL != sh->client);
604   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
605   {
606     p = GNUNET_malloc (sizeof (struct PendingMessage) +
607                        sizeof (struct ClientStartMessage));
608     p->size = sizeof (struct ClientStartMessage);
609     p->is_init = GNUNET_YES;
610     init = (struct ClientStartMessage *) &p[1];
611     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
612     init->header.size = htons (sizeof (struct ClientStartMessage));
613     init->start_flag = htonl (START_FLAG_SCHEDULING);
614     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
615   }
616   do_transmit (sh);
617 }
618
619 /**
620  * delete the current network list
621  */
622
623 static void
624 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
625 {
626   struct ATS_Network * cur = sh->net_head;
627   while (cur != NULL)
628   {
629     GNUNET_CONTAINER_DLL_remove(sh->net_head, sh->net_tail, cur);
630     GNUNET_free (cur);
631     cur = sh->net_head;
632   }
633 }
634
635
636 static int
637 interface_proc (void *cls, const char *name,
638                 int isDefault,
639                 const struct sockaddr *
640                 addr,
641                 const struct sockaddr *
642                 broadcast_addr,
643                 const struct sockaddr *
644                 netmask, socklen_t addrlen)
645 {
646   struct GNUNET_ATS_SchedulingHandle * sh = cls;
647   /* Calculate network */
648   struct ATS_Network *net = NULL;
649
650   /* Skipping IPv4 loopback addresses since we have special check  */
651   if  (addr->sa_family == AF_INET)
652   {
653     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
654
655     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
656        return GNUNET_OK;
657   }
658   /* Skipping IPv6 loopback addresses since we have special check  */
659   if  (addr->sa_family == AF_INET6)
660   {
661     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
662     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
663       return GNUNET_OK;
664   }
665
666   if (addr->sa_family == AF_INET)
667   {
668     struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
669     struct sockaddr_in *netmask4 = (struct sockaddr_in *) netmask;
670     struct sockaddr_in *tmp = NULL;
671     struct sockaddr_in network4;
672
673     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
674     tmp = (struct sockaddr_in *) &net[1];
675     net->network = (struct sockaddr *) &tmp[0];
676     net->netmask = (struct sockaddr *) &tmp[1];
677     net->length = addrlen;
678
679     memset (&network4, 0, sizeof (network4));
680     network4.sin_family = AF_INET;
681 #if HAVE_SOCKADDR_IN_SIN_LEN
682     network4.sin_len = sizeof (network4);
683 #endif
684     network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
685
686     memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
687     memcpy (net->network, &network4, sizeof (struct sockaddr_in));
688   }
689
690   if (addr->sa_family == AF_INET6)
691   {
692     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
693     struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) netmask;
694     struct sockaddr_in6 * tmp = NULL;
695     struct sockaddr_in6 network6;
696
697     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
698     tmp = (struct sockaddr_in6 *) &net[1];
699     net->network = (struct sockaddr *) &tmp[0];
700     net->netmask = (struct sockaddr *) &tmp[1];
701     net->length = addrlen;
702
703     memset (&network6, 0, sizeof (network6));
704     network6.sin6_family = AF_INET6;
705 #if HAVE_SOCKADDR_IN_SIN_LEN
706     network6.sin6_len = sizeof (network6);
707 #endif
708     int c = 0;
709     uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
710     uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
711     uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
712     for (c = 0; c < 4; c++)
713       net_elem[c] = addr_elem[c] & mask_elem[c];
714
715     memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
716     memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
717   }
718
719   /* Store in list */
720   if (net != NULL)
721   {
722 #if VERBOSE_ATS
723     char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
724     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding network `%s', netmask `%s'\n",
725         GNUNET_a2s((struct sockaddr *) net->network, addrlen),
726         netmask);
727     GNUNET_free (netmask);
728 # endif
729     GNUNET_CONTAINER_DLL_insert(sh->net_head, sh->net_tail, net);
730   }
731   return GNUNET_OK;
732 }
733
734
735
736 /**
737  * Periodically get list of addresses
738  * @param cls closure
739  * @param tc Task context
740  */
741 static void
742 get_addresses (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
743 {
744   struct GNUNET_ATS_SchedulingHandle * sh = cls;
745   sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
746   delete_networks (sh);
747   GNUNET_OS_network_interfaces_list(interface_proc, sh);
748   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
749                                                      get_addresses,
750                                                      sh);
751 }
752
753 /**
754  * Returns where the address is located: LAN or WAN or ...
755  * @param sh the scheduling handle
756  * @param addr address
757  * @param addrlen address length
758  * @return location as GNUNET_ATS_Information
759  */
760
761 const struct GNUNET_ATS_Information
762 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
763 {
764   GNUNET_assert (sh != NULL);
765   struct GNUNET_ATS_Information ats;
766   struct ATS_Network * cur = sh->net_head;
767   int type = GNUNET_ATS_NET_UNSPECIFIED;
768
769   if  (addr->sa_family == AF_UNIX)
770   {
771     type = GNUNET_ATS_NET_LOOPBACK;
772   }
773
774   /* IPv4 loopback check */
775   if  (addr->sa_family == AF_INET)
776   {
777     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
778
779     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
780       type = GNUNET_ATS_NET_LOOPBACK;
781   }
782   /* IPv6 loopback check */
783   if  (addr->sa_family == AF_INET6)
784   {
785     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
786     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
787       type = GNUNET_ATS_NET_LOOPBACK;
788   }
789
790   /* Check local networks */
791   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
792   {
793     if (addrlen != cur->length)
794     {
795       cur = cur->next;
796       continue;
797     }
798
799     if (addr->sa_family == AF_INET)
800     {
801       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
802       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
803       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
804
805       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
806       {
807         char * net = GNUNET_strdup (GNUNET_a2s ((const struct sockaddr *) net4, addrlen));
808         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
809             "`%s' is in network `%s'\n",
810             GNUNET_a2s ((const struct sockaddr *)a4, addrlen),
811             net);
812         GNUNET_free (net);
813         type = GNUNET_ATS_NET_LAN;
814       }
815     }
816     if (addr->sa_family == AF_INET6)
817     {
818       struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
819       struct sockaddr_in6 * net6 = (struct sockaddr_in6 *) cur->network;
820       struct sockaddr_in6 * mask6 = (struct sockaddr_in6 *) cur->netmask;
821
822       int res = GNUNET_YES;
823       int c = 0;
824       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
825       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
826       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
827       for (c = 0; c < 4; c++)
828         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
829           res = GNUNET_NO;
830
831       if (res == GNUNET_YES)
832       {
833         char * net = GNUNET_strdup (GNUNET_a2s ((const struct sockaddr *) net6, addrlen));
834         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' is in network `%s'\n",
835               GNUNET_a2s ((const struct sockaddr *) a6, addrlen),
836               net);
837         GNUNET_free (net);
838         type = GNUNET_ATS_NET_LAN;
839       }
840     }
841     cur = cur->next;
842   }
843
844   /* no local network found for this address, default: WAN */
845   if (type == GNUNET_ATS_NET_UNSPECIFIED)
846     type = GNUNET_ATS_NET_WAN;
847
848 #if VERBOSE
849   const char * range;
850   switch (type) {
851     case GNUNET_ATS_NET_WAN:
852         range = "WAN";
853       break;
854     case GNUNET_ATS_NET_LAN:
855         range = "LAN";
856       break;
857     case GNUNET_ATS_NET_LOOPBACK:
858         range = "LOOPBACK";
859       break;
860     default:
861
862       break;
863   }
864 #endif
865
866   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
867   ats.value = htonl (type);
868   return (const struct GNUNET_ATS_Information) ats;
869 }
870
871 /**
872  * Initialize the ATS subsystem.
873  *
874  * @param cfg configuration to use
875  * @param suggest_cb notification to call whenever the suggestation changed
876  * @param suggest_cb_cls closure for 'suggest_cb'
877  * @return ats context
878  */
879 struct GNUNET_ATS_SchedulingHandle *
880 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
881                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
882                             void *suggest_cb_cls)
883 {
884   struct GNUNET_ATS_SchedulingHandle *sh;
885
886   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
887   sh->cfg = cfg;
888   sh->suggest_cb = suggest_cb;
889   sh->suggest_cb_cls = suggest_cb_cls;
890   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
891   GNUNET_OS_network_interfaces_list(interface_proc, sh);
892   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
893       get_addresses,
894       sh);
895   reconnect (sh);
896   return sh;
897 }
898
899
900 /**
901  * Client is done with ATS scheduling, release resources.
902  *
903  * @param sh handle to release
904  */
905 void
906 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
907 {
908   struct PendingMessage *p;
909
910   while (NULL != (p = sh->pending_head))
911   {
912     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
913     GNUNET_free (p);
914   }
915   if (NULL != sh->client)
916   {
917     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
918     sh->client = NULL;
919   }
920   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
921   {
922     GNUNET_SCHEDULER_cancel (sh->task);
923     sh->task = GNUNET_SCHEDULER_NO_TASK;
924   }
925
926   delete_networks (sh);
927   if (sh->interface_task != GNUNET_SCHEDULER_NO_TASK)
928   {
929     GNUNET_SCHEDULER_cancel(sh->interface_task);
930     sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
931   }
932   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
933   GNUNET_free (sh);
934   sh = NULL;
935 }
936
937
938 /**
939  * We would like to establish a new connection with a peer.  ATS
940  * should suggest a good address to begin with.
941  *
942  * @param sh handle
943  * @param peer identity of the peer we need an address for
944  */
945 void
946 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
947                             const struct GNUNET_PeerIdentity *peer)
948 {
949   struct PendingMessage *p;
950   struct RequestAddressMessage *m;
951
952   p = GNUNET_malloc (sizeof (struct PendingMessage) +
953                      sizeof (struct RequestAddressMessage));
954   p->size = sizeof (struct RequestAddressMessage);
955   p->is_init = GNUNET_NO;
956   m = (struct RequestAddressMessage *) &p[1];
957   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
958   m->header.size = htons (sizeof (struct RequestAddressMessage));
959   m->reserved = htonl (0);
960   m->peer = *peer;
961   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
962   do_transmit (sh);
963 }
964
965
966 /**
967  * We would like to stop receiving address updates for this peer
968  *
969  * @param sh handle
970  * @param peer identity of the peer
971  */
972 void
973 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
974                                    const struct GNUNET_PeerIdentity *peer)
975 {
976   struct PendingMessage *p;
977   struct RequestAddressMessage *m;
978
979   p = GNUNET_malloc (sizeof (struct PendingMessage) +
980                      sizeof (struct RequestAddressMessage));
981   p->size = sizeof (struct RequestAddressMessage);
982   p->is_init = GNUNET_NO;
983   m = (struct RequestAddressMessage *) &p[1];
984   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
985   m->header.size = htons (sizeof (struct RequestAddressMessage));
986   m->reserved = htonl (0);
987   m->peer = *peer;
988   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
989   do_transmit (sh);
990 }
991
992 /**
993  * We have updated performance statistics for a given address.  Note
994  * that this function can be called for addresses that are currently
995  * in use as well as addresses that are valid but not actively in use.
996  * Furthermore, the peer may not even be connected to us right now (in
997  * which case the call may be ignored or the information may be stored
998  * for later use).  Update bandwidth assignments.
999  *
1000  * @param sh handle
1001  * @param address the address
1002  * @param session session handle (if available)
1003  * @param ats performance data for the address
1004  * @param ats_count number of performance records in 'ats'
1005  */
1006 void
1007 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1008                            const struct GNUNET_HELLO_Address *address,
1009                            struct Session *session,
1010                            const struct GNUNET_ATS_Information *ats,
1011                            uint32_t ats_count)
1012 {
1013   struct PendingMessage *p;
1014   struct AddressUpdateMessage *m;
1015   struct GNUNET_ATS_Information *am;
1016   char *pm;
1017   size_t namelen;
1018   size_t msize;
1019
1020   if (address == NULL)
1021   {
1022     GNUNET_break (0);
1023     return;
1024   }
1025   if ((address == NULL) && (session == NULL))
1026   {
1027     GNUNET_break (0);
1028     return;
1029   }
1030
1031   namelen =
1032       (address->transport_name ==
1033        NULL) ? 0 : strlen (address->transport_name) + 1;
1034   msize =
1035       sizeof (struct AddressUpdateMessage) + address->address_length +
1036       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1037   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1038       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1039       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1040       (ats_count >=
1041        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1042   {
1043     GNUNET_break (0);
1044     return;
1045   }
1046
1047   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1048   p->size = msize;
1049   p->is_init = GNUNET_NO;
1050   m = (struct AddressUpdateMessage *) &p[1];
1051   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1052   m->header.size = htons (msize);
1053   m->ats_count = htonl (ats_count);
1054   m->peer = address->peer;
1055   m->address_length = htons (address->address_length);
1056   m->plugin_name_length = htons (namelen);
1057   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1058   am = (struct GNUNET_ATS_Information *) &m[1];
1059   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1060   pm = (char *) &am[ats_count];
1061   memcpy (pm, address->address, address->address_length);
1062   memcpy (&pm[address->address_length], address->transport_name, namelen);
1063   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1064   do_transmit (sh);
1065 }
1066
1067
1068 /**
1069  * An address is now in use or not used any more.
1070  *
1071  * @param sh handle
1072  * @param address the address
1073  * @param session session handle
1074  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
1075  * if address is not used any more
1076  */
1077 void
1078 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1079                            const struct GNUNET_HELLO_Address *address,
1080                            struct Session *session, int in_use)
1081 {
1082   struct PendingMessage *p;
1083   struct AddressUseMessage *m;
1084   char *pm;
1085   size_t namelen;
1086   size_t msize;
1087
1088   GNUNET_assert (NULL != address);
1089   namelen =
1090       (address->transport_name ==
1091        NULL) ? 0 : strlen (address->transport_name) + 1;
1092   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
1093   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1094       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1095       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1096   {
1097     GNUNET_break (0);
1098     return;
1099   }
1100
1101   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1102   p->size = msize;
1103   p->is_init = GNUNET_NO;
1104   m = (struct AddressUseMessage *) &p[1];
1105   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1106   m->header.size = htons (msize);
1107   m->peer = address->peer;
1108   m->in_use = htons (in_use);
1109   m->address_length = htons (address->address_length);
1110   m->plugin_name_length = htons (namelen);
1111   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1112   pm = (char *) &m[1];
1113   memcpy (pm, address->address, address->address_length);
1114   memcpy (&pm[address->address_length], address->transport_name, namelen);
1115   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1116
1117   do_transmit (sh);
1118 }
1119
1120 /**
1121  * A session got destroyed, stop including it as a valid address.
1122  *
1123  * @param sh handle
1124  * @param address the address
1125  * @param session session handle that is no longer valid
1126  */
1127 void
1128 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1129                               const struct GNUNET_HELLO_Address *address,
1130                               struct Session *session)
1131 {
1132   struct PendingMessage *p;
1133   struct AddressDestroyedMessage *m;
1134   char *pm;
1135   size_t namelen;
1136   size_t msize;
1137   uint32_t session_id;
1138
1139   GNUNET_assert (address->transport_name != NULL);
1140   namelen = strlen (address->transport_name) + 1;
1141   GNUNET_assert (namelen > 1);
1142   msize =
1143       sizeof (struct AddressDestroyedMessage) + address->address_length +
1144       namelen;
1145   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1146       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1147       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1148   {
1149     GNUNET_break (0);
1150     return;
1151   }
1152
1153   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1154   p->size = msize;
1155   p->is_init = GNUNET_NO;
1156   m = (struct AddressDestroyedMessage *) &p[1];
1157   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1158   m->header.size = htons (msize);
1159   m->reserved = htonl (0);
1160   m->peer = address->peer;
1161   m->address_length = htons (address->address_length);
1162   m->plugin_name_length = htons (namelen);
1163   session_id = get_session_id (sh, session, &address->peer);
1164   m->session_id = htonl (session_id);
1165   pm = (char *) &m[1];
1166   memcpy (pm, address->address, address->address_length);
1167   memcpy (&pm[address->address_length], address->transport_name, namelen);
1168   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1169   do_transmit (sh);
1170   remove_session (sh, session_id, &address->peer);
1171 }
1172
1173 /* end of ats_api_scheduling.c */