fixing mantis 2098:
[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 #if DEBUG_ATS
323   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Find session %u from peer %s in %p\n",
324               (unsigned int) session_id, GNUNET_i2s (peer), sh);
325 #endif
326   if (session_id >= sh->session_array_size)
327   {
328     GNUNET_break (0);
329     return NULL;
330   }
331   if (0 == session_id)
332     return NULL;
333   if (sh->session_array[session_id].session == NULL)
334   {
335     GNUNET_break (0 ==
336                   memcmp (peer, &sh->session_array[session_id].peer,
337                           sizeof (struct GNUNET_PeerIdentity)));
338     return NULL;
339   }
340
341   if (0 !=
342       memcmp (peer, &sh->session_array[session_id].peer,
343               sizeof (struct GNUNET_PeerIdentity)))
344   {
345     GNUNET_break (0);
346     sh->reconnect = GNUNET_YES;
347     return NULL;
348   }
349   return sh->session_array[session_id].session;
350 }
351
352
353 /**
354  * Get the ID for the given session object.  If we do not have an ID for
355  * the given session object, allocate one.
356  *
357  * @param sh our handle
358  * @param session session object
359  * @param peer peer the session belongs to
360  * @return the session id
361  */
362 static uint32_t
363 get_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
364                 const struct GNUNET_PeerIdentity *peer)
365 {
366   unsigned int i;
367   unsigned int f;
368
369 #if DEBUG_ATS
370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371               "Get session ID for session %p from peer %s in %p\n", session,
372               GNUNET_i2s (peer), sh);
373 #endif
374   if (NULL == session)
375     return 0;
376   f = 0;
377   for (i = 1; i < sh->session_array_size; i++)
378   {
379     if (session == sh->session_array[i].session)
380     {
381       GNUNET_assert (0 ==
382                      memcmp (peer, &sh->session_array[i].peer,
383                              sizeof (struct GNUNET_PeerIdentity)));
384       return i;
385     }
386     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
387       f = i;
388   }
389   if (f == 0)
390   {
391     f = sh->session_array_size;
392     GNUNET_array_grow (sh->session_array, sh->session_array_size,
393                        sh->session_array_size * 2);
394   }
395   GNUNET_assert (f > 0);
396   sh->session_array[f].session = session;
397   sh->session_array[f].peer = *peer;
398   sh->session_array[f].slot_used = GNUNET_YES;
399 #if DEBUG_ATS
400   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401               "Assigning session ID %u for session %p of peer %s in %p\n", f,
402               session, GNUNET_i2s (peer), sh);
403 #endif
404   return f;
405 }
406
407
408 /**
409  * Remove the session of the given session ID from the session
410  * table (it is no longer valid).
411  *
412  * @param sh our handle
413  * @param session_id identifies session that is no longer valid
414  * @param peer peer the session belongs to
415  */
416 static void
417 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
418                 const struct GNUNET_PeerIdentity *peer)
419 {
420 #if DEBUG_ATS
421   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422               "Remove sessionID %u from peer %s in %p\n",
423               (unsigned int) session_id, GNUNET_i2s (peer), sh);
424 #endif
425   if (0 == session_id)
426     return;
427   GNUNET_assert (session_id < sh->session_array_size);
428   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
429   GNUNET_assert (0 ==
430                  memcmp (peer, &sh->session_array[session_id].peer,
431                          sizeof (struct GNUNET_PeerIdentity)));
432   sh->session_array[session_id].session = NULL;
433 }
434
435
436 /**
437  * Release the session slot from the session table (ATS service is
438  * also done using it).
439  *
440  * @param sh our handle
441  * @param session_id identifies session that is no longer valid
442  * @param peer peer the session belongs to
443  */
444 static void
445 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
446                  const struct GNUNET_PeerIdentity *peer)
447 {
448 #if DEBUG_ATS
449   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450               "Release sessionID %u from peer %s in %p\n",
451               (unsigned int) session_id, GNUNET_i2s (peer), sh);
452 #endif
453   if (session_id >= sh->session_array_size)
454   {
455     GNUNET_break (0);
456     sh->reconnect = GNUNET_YES;
457     return;
458   }
459
460   /* this slot should have been removed from remove_session before */
461   GNUNET_assert (sh->session_array[session_id].session == NULL);
462
463   if (0 !=
464       memcmp (peer, &sh->session_array[session_id].peer,
465               sizeof (struct GNUNET_PeerIdentity)))
466   {
467     GNUNET_break (0);
468     sh->reconnect = GNUNET_YES;
469     return;
470   }
471   sh->session_array[session_id].slot_used = GNUNET_NO;
472   memset (&sh->session_array[session_id].peer, 0,
473           sizeof (struct GNUNET_PeerIdentity));
474 }
475
476
477 static void
478 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
479                          const struct SessionReleaseMessage *srm)
480 {
481   release_session (sh, ntohl (srm->session_id), &srm->peer);
482 }
483
484
485 /**
486  * Type of a function to call when we receive a message
487  * from the service.
488  *
489  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
490  * @param msg message received, NULL on timeout or fatal error
491  */
492 static void
493 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
494 {
495   struct GNUNET_ATS_SchedulingHandle *sh = cls;
496   const struct AddressSuggestionMessage *m;
497   const struct GNUNET_ATS_Information *atsi;
498   const char *plugin_address;
499   const char *plugin_name;
500   uint16_t plugin_address_length;
501   uint16_t plugin_name_length;
502   uint32_t ats_count;
503   struct GNUNET_HELLO_Address address;
504   struct Session *s;
505
506   if (NULL == msg)
507   {
508     force_reconnect (sh);
509     return;
510   }
511   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
512       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
513   {
514     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
515     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
516                            GNUNET_TIME_UNIT_FOREVER_REL);
517     if (GNUNET_YES == sh->reconnect)
518       force_reconnect (sh);
519     return;
520   }
521   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
522       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
523   {
524     GNUNET_break (0);
525     force_reconnect (sh);
526     return;
527   }
528   m = (const struct AddressSuggestionMessage *) msg;
529   ats_count = ntohl (m->ats_count);
530   plugin_address_length = ntohs (m->address_length);
531   atsi = (const struct GNUNET_ATS_Information *) &m[1];
532   plugin_address = (const char *) &atsi[ats_count];
533   plugin_name = &plugin_address[plugin_address_length];
534   plugin_name_length = ntohs (m->plugin_name_length);
535   if ((plugin_address_length + plugin_name_length +
536        ats_count * sizeof (struct GNUNET_ATS_Information) +
537        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
538       (ats_count >
539        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
540       || (plugin_name[plugin_name_length - 1] != '\0'))
541   {
542     GNUNET_break (0);
543     force_reconnect (sh);
544     return;
545   }
546   uint32_t session_id = ntohl (m->session_id);
547
548   if (session_id == 0)
549     s = NULL;
550   else
551   {
552     s = find_session (sh, session_id, &m->peer);
553     if (s == NULL)
554     {
555 #if DEBUG_ATS
556       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
557                   "ATS tries to use outdated session `%s'\n",
558                   GNUNET_i2s (&m->peer));
559 #endif
560       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
561                              GNUNET_TIME_UNIT_FOREVER_REL);
562       return;
563     }
564   }
565   address.peer = m->peer;
566   address.address = plugin_address;
567   address.address_length = plugin_address_length;
568   address.transport_name = plugin_name;
569
570   if ((s == NULL) && (0 == address.address_length))
571   {
572     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
574                 GNUNET_i2s (&address.peer), address.transport_name,
575                 plugin_address_length, session_id);
576     GNUNET_break_op (0);
577     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
578                            GNUNET_TIME_UNIT_FOREVER_REL);
579     return;
580   }
581
582   sh->suggest_cb (sh->suggest_cb_cls, &address, s, m->bandwidth_out,
583                   m->bandwidth_in, atsi, ats_count);
584
585   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
586                          GNUNET_TIME_UNIT_FOREVER_REL);
587   if (GNUNET_YES == sh->reconnect)
588     force_reconnect (sh);
589 }
590
591
592 /**
593  * Re-establish the connection to the ATS service.
594  *
595  * @param sh handle to use to re-connect.
596  */
597 static void
598 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
599 {
600   struct PendingMessage *p;
601   struct ClientStartMessage *init;
602
603   GNUNET_assert (NULL == sh->client);
604   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
605   GNUNET_assert (NULL != sh->client);
606   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
607   {
608     p = GNUNET_malloc (sizeof (struct PendingMessage) +
609                        sizeof (struct ClientStartMessage));
610     p->size = sizeof (struct ClientStartMessage);
611     p->is_init = GNUNET_YES;
612     init = (struct ClientStartMessage *) &p[1];
613     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
614     init->header.size = htons (sizeof (struct ClientStartMessage));
615     init->start_flag = htonl (START_FLAG_SCHEDULING);
616     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
617   }
618   do_transmit (sh);
619 }
620
621 /**
622  * delete the current network list
623  */
624
625 static void
626 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
627 {
628   struct ATS_Network * cur = sh->net_head;
629   while (cur != NULL)
630   {
631     GNUNET_CONTAINER_DLL_remove(sh->net_head, sh->net_tail, cur);
632     GNUNET_free (cur);
633     cur = sh->net_head;
634   }
635 }
636
637
638 static int
639 interface_proc (void *cls, const char *name,
640                 int isDefault,
641                 const struct sockaddr *
642                 addr,
643                 const struct sockaddr *
644                 broadcast_addr,
645                 const struct sockaddr *
646                 netmask, socklen_t addrlen)
647 {
648   struct GNUNET_ATS_SchedulingHandle * sh = cls;
649   /* Calculate network */
650   struct ATS_Network *net = NULL;
651
652   /* Skipping IPv4 loopback addresses since we have special check  */
653   if  (addr->sa_family == AF_INET)
654   {
655     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
656
657     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
658        return GNUNET_OK;
659   }
660   /* Skipping IPv6 loopback addresses since we have special check  */
661   if  (addr->sa_family == AF_INET6)
662   {
663     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
664     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
665       return GNUNET_OK;
666   }
667
668   if (addr->sa_family == AF_INET)
669   {
670     struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
671     struct sockaddr_in *netmask4 = (struct sockaddr_in *) netmask;
672     struct sockaddr_in *tmp = NULL;
673     struct sockaddr_in network4;
674
675     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
676     tmp = (struct sockaddr_in *) &net[1];
677     net->network = (struct sockaddr *) &tmp[0];
678     net->netmask = (struct sockaddr *) &tmp[1];
679     net->length = addrlen;
680
681     memset (&network4, 0, sizeof (network4));
682     network4.sin_family = AF_INET;
683 #if HAVE_SOCKADDR_IN_SIN_LEN
684     network4.sin_len = sizeof (network4);
685 #endif
686     network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
687
688     memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
689     memcpy (net->network, &network4, sizeof (struct sockaddr_in));
690   }
691
692   if (addr->sa_family == AF_INET6)
693   {
694     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
695     struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) netmask;
696     struct sockaddr_in6 * tmp = NULL;
697     struct sockaddr_in6 network6;
698
699     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
700     tmp = (struct sockaddr_in6 *) &net[1];
701     net->network = (struct sockaddr *) &tmp[0];
702     net->netmask = (struct sockaddr *) &tmp[1];
703     net->length = addrlen;
704
705     memset (&network6, 0, sizeof (network6));
706     network6.sin6_family = AF_INET6;
707 #if HAVE_SOCKADDR_IN_SIN_LEN
708     network6.sin6_len = sizeof (network6);
709 #endif
710     int c = 0;
711     uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
712     uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
713     uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
714     for (c = 0; c < 4; c++)
715       net_elem[c] = addr_elem[c] & mask_elem[c];
716
717     memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
718     memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
719   }
720
721   /* Store in list */
722   if (net != NULL)
723   {
724 #if VERBOSE_ATS
725     char * netmask = strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
726     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding network `%s', netmask `%s'\n",
727         GNUNET_a2s((struct sockaddr *) net->network, addrlen),
728         netmask);
729     GNUNET_free (netmask);
730 # endif
731     GNUNET_CONTAINER_DLL_insert(sh->net_head, sh->net_tail, net);
732   }
733   return GNUNET_OK;
734 }
735
736
737
738 /**
739  * Periodically get list of addresses
740  * @param cls closure
741  * @param tc Task context
742  */
743 static void
744 get_addresses (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
745 {
746   struct GNUNET_ATS_SchedulingHandle * sh = cls;
747   sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
748   delete_networks (sh);
749   GNUNET_OS_network_interfaces_list(interface_proc, sh);
750   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
751                                                      get_addresses,
752                                                      sh);
753 }
754
755 /**
756  * Returns where the address is located: LAN or WAN or ...
757  * @param addr address
758  * @param addrlen address length
759  * @return location as GNUNET_ATS_Information
760  */
761
762 struct GNUNET_ATS_Information
763 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
764 {
765   GNUNET_assert (sh != NULL);
766   struct GNUNET_ATS_Information ats;
767   struct ATS_Network * cur = sh->net_head;
768   int type = GNUNET_ATS_NET_UNSPECIFIED;
769
770   if  (addr->sa_family == AF_UNIX)
771   {
772     type = GNUNET_ATS_NET_LOOPBACK;
773   }
774
775   /* IPv4 loopback check */
776   if  (addr->sa_family == AF_INET)
777   {
778     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
779
780     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
781       type = GNUNET_ATS_NET_LOOPBACK;
782   }
783   /* IPv6 loopback check */
784   if  (addr->sa_family == AF_INET6)
785   {
786     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
787     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
788       type = GNUNET_ATS_NET_LOOPBACK;
789   }
790
791   /* Check local networks */
792   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
793   {
794     if (addrlen != cur->length)
795     {
796       cur = cur->next;
797       continue;
798     }
799
800     if (addr->sa_family == AF_INET)
801     {
802       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
803       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
804       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
805
806       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
807       {
808         char * net = strdup (GNUNET_a2s ((const struct sockaddr *) net4, addrlen));
809         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%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 = 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   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "`%s' is in network `%s'\n",
865         GNUNET_a2s ((const struct sockaddr *) addr, addrlen),
866         range);
867 #endif
868
869   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
870   ats.value = htonl (type);
871   return ats;
872 }
873
874 /**
875  * Initialize the ATS subsystem.
876  *
877  * @param cfg configuration to use
878  * @param suggest_cb notification to call whenever the suggestation changed
879  * @param suggest_cb_cls closure for 'suggest_cb'
880  * @return ats context
881  */
882 struct GNUNET_ATS_SchedulingHandle *
883 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
884                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
885                             void *suggest_cb_cls)
886 {
887   struct GNUNET_ATS_SchedulingHandle *sh;
888
889   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
890   sh->cfg = cfg;
891   sh->suggest_cb = suggest_cb;
892   sh->suggest_cb_cls = suggest_cb_cls;
893   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
894   GNUNET_OS_network_interfaces_list(interface_proc, sh);
895   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
896       get_addresses,
897       sh);
898   reconnect (sh);
899   return sh;
900 }
901
902
903 /**
904  * Client is done with ATS scheduling, release resources.
905  *
906  * @param sh handle to release
907  */
908 void
909 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
910 {
911   struct PendingMessage *p;
912
913   while (NULL != (p = sh->pending_head))
914   {
915     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
916     GNUNET_free (p);
917   }
918   if (NULL != sh->client)
919   {
920     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
921     sh->client = NULL;
922   }
923   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
924   {
925     GNUNET_SCHEDULER_cancel (sh->task);
926     sh->task = GNUNET_SCHEDULER_NO_TASK;
927   }
928
929   delete_networks (sh);
930   if (sh->interface_task != GNUNET_SCHEDULER_NO_TASK)
931   {
932     GNUNET_SCHEDULER_cancel(sh->interface_task);
933     sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
934   }
935   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
936   GNUNET_free (sh);
937   sh = NULL;
938 }
939
940
941 /**
942  * We would like to establish a new connection with a peer.  ATS
943  * should suggest a good address to begin with.
944  *
945  * @param sh handle
946  * @param peer identity of the peer we need an address for
947  */
948 void
949 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
950                             const struct GNUNET_PeerIdentity *peer)
951 {
952   struct PendingMessage *p;
953   struct RequestAddressMessage *m;
954
955   p = GNUNET_malloc (sizeof (struct PendingMessage) +
956                      sizeof (struct RequestAddressMessage));
957   p->size = sizeof (struct RequestAddressMessage);
958   p->is_init = GNUNET_NO;
959   m = (struct RequestAddressMessage *) &p[1];
960   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
961   m->header.size = htons (sizeof (struct RequestAddressMessage));
962   m->reserved = htonl (0);
963   m->peer = *peer;
964   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
965   do_transmit (sh);
966 }
967
968
969 /**
970  * We would like to stop receiving address updates for this peer
971  *
972  * @param sh handle
973  * @param peer identity of the peer
974  */
975 void
976 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
977                                    const struct GNUNET_PeerIdentity *peer)
978 {
979   struct PendingMessage *p;
980   struct RequestAddressMessage *m;
981
982   p = GNUNET_malloc (sizeof (struct PendingMessage) +
983                      sizeof (struct RequestAddressMessage));
984   p->size = sizeof (struct RequestAddressMessage);
985   p->is_init = GNUNET_NO;
986   m = (struct RequestAddressMessage *) &p[1];
987   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
988   m->header.size = htons (sizeof (struct RequestAddressMessage));
989   m->reserved = htonl (0);
990   m->peer = *peer;
991   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
992   do_transmit (sh);
993 }
994
995 /**
996  * We have updated performance statistics for a given address.  Note
997  * that this function can be called for addresses that are currently
998  * in use as well as addresses that are valid but not actively in use.
999  * Furthermore, the peer may not even be connected to us right now (in
1000  * which case the call may be ignored or the information may be stored
1001  * for later use).  Update bandwidth assignments.
1002  *
1003  * @param sh handle
1004  * @param address the address
1005  * @param session session handle (if available)
1006  * @param ats performance data for the address
1007  * @param ats_count number of performance records in 'ats'
1008  */
1009 void
1010 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1011                            const struct GNUNET_HELLO_Address *address,
1012                            struct Session *session,
1013                            const struct GNUNET_ATS_Information *ats,
1014                            uint32_t ats_count)
1015 {
1016   struct PendingMessage *p;
1017   struct AddressUpdateMessage *m;
1018   struct GNUNET_ATS_Information *am;
1019   char *pm;
1020   size_t namelen;
1021   size_t msize;
1022
1023   if ((address == NULL) && (session == NULL))
1024   {
1025     GNUNET_break (0);
1026     return;
1027   }
1028
1029   namelen =
1030       (address->transport_name ==
1031        NULL) ? 0 : strlen (address->transport_name) + 1;
1032   msize =
1033       sizeof (struct AddressUpdateMessage) + address->address_length +
1034       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1035   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1036       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1037       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1038       (ats_count >=
1039        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1040   {
1041     GNUNET_break (0);
1042     return;
1043   }
1044
1045   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1046   p->size = msize;
1047   p->is_init = GNUNET_NO;
1048   m = (struct AddressUpdateMessage *) &p[1];
1049   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1050   m->header.size = htons (msize);
1051   m->ats_count = htonl (ats_count);
1052   m->peer = address->peer;
1053   m->address_length = htons (address->address_length);
1054   m->plugin_name_length = htons (namelen);
1055   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1056   am = (struct GNUNET_ATS_Information *) &m[1];
1057   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1058   pm = (char *) &am[ats_count];
1059   memcpy (pm, address->address, address->address_length);
1060   memcpy (&pm[address->address_length], address->transport_name, namelen);
1061   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1062   do_transmit (sh);
1063 }
1064
1065
1066 /**
1067  * An address is now in use or not used any more.
1068  *
1069  * @param sh handle
1070  * @param address the address
1071  * @param session session handle
1072  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
1073  * if address is not used any more
1074  */
1075 void
1076 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1077                            const struct GNUNET_HELLO_Address *address,
1078                            struct Session *session, int in_use)
1079 {
1080   struct PendingMessage *p;
1081   struct AddressUseMessage *m;
1082   char *pm;
1083   size_t namelen;
1084   size_t msize;
1085
1086   GNUNET_assert (NULL != address);
1087   namelen =
1088       (address->transport_name ==
1089        NULL) ? 0 : strlen (address->transport_name) + 1;
1090   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
1091   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1092       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1093       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1094   {
1095     GNUNET_break (0);
1096     return;
1097   }
1098
1099   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1100   p->size = msize;
1101   p->is_init = GNUNET_NO;
1102   m = (struct AddressUseMessage *) &p[1];
1103   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1104   m->header.size = htons (msize);
1105   m->peer = address->peer;
1106   m->in_use = htons (in_use);
1107   m->address_length = htons (address->address_length);
1108   m->plugin_name_length = htons (namelen);
1109   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1110   pm = (char *) &m[1];
1111   memcpy (pm, address->address, address->address_length);
1112   memcpy (&pm[address->address_length], address->transport_name, namelen);
1113   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1114
1115   do_transmit (sh);
1116 }
1117
1118 /**
1119  * A session got destroyed, stop including it as a valid address.
1120  *
1121  * @param sh handle
1122  * @param address the address
1123  * @param session session handle that is no longer valid
1124  */
1125 void
1126 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1127                               const struct GNUNET_HELLO_Address *address,
1128                               struct Session *session)
1129 {
1130   struct PendingMessage *p;
1131   struct AddressDestroyedMessage *m;
1132   char *pm;
1133   size_t namelen;
1134   size_t msize;
1135   uint32_t session_id;
1136
1137   GNUNET_assert (address->transport_name != NULL);
1138   namelen = strlen (address->transport_name) + 1;
1139   GNUNET_assert (namelen > 1);
1140   msize =
1141       sizeof (struct AddressDestroyedMessage) + address->address_length +
1142       namelen;
1143   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1144       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1145       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1146   {
1147     GNUNET_break (0);
1148     return;
1149   }
1150
1151   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1152   p->size = msize;
1153   p->is_init = GNUNET_NO;
1154   m = (struct AddressDestroyedMessage *) &p[1];
1155   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1156   m->header.size = htons (msize);
1157   m->reserved = htonl (0);
1158   m->peer = address->peer;
1159   m->address_length = htons (address->address_length);
1160   m->plugin_name_length = htons (namelen);
1161   session_id = get_session_id (sh, session, &address->peer);
1162   m->session_id = htonl (session_id);
1163   pm = (char *) &m[1];
1164   memcpy (pm, address->address, address->address_length);
1165   memcpy (&pm[address->address_length], address->transport_name, namelen);
1166   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1167   do_transmit (sh);
1168   remove_session (sh, session_id, &address->peer);
1169 }
1170
1171 /* end of ats_api_scheduling.c */