fixes for mantis #1988
[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     network4.sin_family = AF_INET;
682     network4.sin_port = htons (0);
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     network6.sin6_family = AF_INET6;
706     network6.sin6_port = htons (0);
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     char * netmask = strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
725     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding network `%s', netmask `%s'\n",
726         GNUNET_a2s((struct sockaddr *) net->network, addrlen),
727         netmask);
728     GNUNET_free (netmask);
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 addr address
756  * @param addrlen address length
757  * @return location as GNUNET_ATS_Information
758  */
759
760 struct GNUNET_ATS_Information
761 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
762 {
763   GNUNET_assert (sh != NULL);
764   struct GNUNET_ATS_Information ats;
765   struct ATS_Network * cur = sh->net_head;
766   int type = GNUNET_ATS_NET_UNSPECIFIED;
767
768   if  (addr->sa_family == AF_UNIX)
769   {
770     type = GNUNET_ATS_NET_LOOPBACK;
771   }
772
773   /* IPv4 loopback check */
774   if  (addr->sa_family == AF_INET)
775   {
776     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
777
778     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
779       type = GNUNET_ATS_NET_LOOPBACK;
780   }
781   /* IPv6 loopback check */
782   if  (addr->sa_family == AF_INET6)
783   {
784     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
785     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
786       type = GNUNET_ATS_NET_LOOPBACK;
787   }
788
789   /* Check local networks */
790   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
791   {
792     if (addrlen != cur->length)
793     {
794       cur = cur->next;
795       continue;
796     }
797
798     if (addr->sa_family == AF_INET)
799     {
800       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
801       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
802       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
803
804       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
805       {
806         char * net = strdup (GNUNET_a2s ((const struct sockaddr *) net4, addrlen));
807         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' is in network `%s'\n",
808             GNUNET_a2s ((const struct sockaddr *)a4, addrlen),
809             net);
810         GNUNET_free (net);
811         type = GNUNET_ATS_NET_LAN;
812       }
813     }
814     if (addr->sa_family == AF_INET6)
815     {
816       struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
817       struct sockaddr_in6 * net6 = (struct sockaddr_in6 *) cur->network;
818       struct sockaddr_in6 * mask6 = (struct sockaddr_in6 *) cur->netmask;
819
820       int res = GNUNET_YES;
821       int c = 0;
822       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
823       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
824       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
825       for (c = 0; c < 4; c++)
826         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
827           res = GNUNET_NO;
828
829       if (res == GNUNET_YES)
830       {
831         char * net = strdup (GNUNET_a2s ((const struct sockaddr *) net6, addrlen));
832         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' is in network `%s'\n",
833               GNUNET_a2s ((const struct sockaddr *) a6, addrlen),
834               net);
835         GNUNET_free (net);
836         type = GNUNET_ATS_NET_LAN;
837       }
838     }
839     cur = cur->next;
840   }
841
842   /* no local network found for this address, default: WAN */
843   if (type == GNUNET_ATS_NET_UNSPECIFIED)
844     type = GNUNET_ATS_NET_WAN;
845
846 #if VERBOSE
847   const char * range;
848   switch (type) {
849     case GNUNET_ATS_NET_WAN:
850         range = "WAN";
851       break;
852     case GNUNET_ATS_NET_LAN:
853         range = "LAN";
854       break;
855     case GNUNET_ATS_NET_LOOPBACK:
856         range = "LOOPBACK";
857       break;
858     default:
859
860       break;
861   }
862   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "`%s' is in network `%s'\n",
863         GNUNET_a2s ((const struct sockaddr *) addr, addrlen),
864         range);
865 #endif
866
867   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
868   ats.value = htonl (type);
869   return ats;
870 }
871
872 /**
873  * Initialize the ATS subsystem.
874  *
875  * @param cfg configuration to use
876  * @param suggest_cb notification to call whenever the suggestation changed
877  * @param suggest_cb_cls closure for 'suggest_cb'
878  * @return ats context
879  */
880 struct GNUNET_ATS_SchedulingHandle *
881 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
882                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
883                             void *suggest_cb_cls)
884 {
885   struct GNUNET_ATS_SchedulingHandle *sh;
886
887   sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SchedulingHandle));
888   sh->cfg = cfg;
889   sh->suggest_cb = suggest_cb;
890   sh->suggest_cb_cls = suggest_cb_cls;
891   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
892   GNUNET_OS_network_interfaces_list(interface_proc, sh);
893   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
894       get_addresses,
895       sh);
896   reconnect (sh);
897   return sh;
898 }
899
900
901 /**
902  * Client is done with ATS scheduling, release resources.
903  *
904  * @param sh handle to release
905  */
906 void
907 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
908 {
909   struct PendingMessage *p;
910
911   while (NULL != (p = sh->pending_head))
912   {
913     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
914     GNUNET_free (p);
915   }
916   if (NULL != sh->client)
917   {
918     GNUNET_CLIENT_disconnect (sh->client, GNUNET_NO);
919     sh->client = NULL;
920   }
921   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
922   {
923     GNUNET_SCHEDULER_cancel (sh->task);
924     sh->task = GNUNET_SCHEDULER_NO_TASK;
925   }
926
927   delete_networks (sh);
928   if (sh->interface_task != GNUNET_SCHEDULER_NO_TASK)
929   {
930     GNUNET_SCHEDULER_cancel(sh->interface_task);
931     sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
932   }
933   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
934   GNUNET_free (sh);
935   sh = NULL;
936 }
937
938
939 /**
940  * We would like to establish a new connection with a peer.  ATS
941  * should suggest a good address to begin with.
942  *
943  * @param sh handle
944  * @param peer identity of the peer we need an address for
945  */
946 void
947 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
948                             const struct GNUNET_PeerIdentity *peer)
949 {
950   struct PendingMessage *p;
951   struct RequestAddressMessage *m;
952
953   p = GNUNET_malloc (sizeof (struct PendingMessage) +
954                      sizeof (struct RequestAddressMessage));
955   p->size = sizeof (struct RequestAddressMessage);
956   p->is_init = GNUNET_NO;
957   m = (struct RequestAddressMessage *) &p[1];
958   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
959   m->header.size = htons (sizeof (struct RequestAddressMessage));
960   m->reserved = htonl (0);
961   m->peer = *peer;
962   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
963   do_transmit (sh);
964 }
965
966
967 /**
968  * We would like to stop receiving address updates for this peer
969  *
970  * @param sh handle
971  * @param peer identity of the peer
972  */
973 void
974 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
975                                    const struct GNUNET_PeerIdentity *peer)
976 {
977   struct PendingMessage *p;
978   struct RequestAddressMessage *m;
979
980   p = GNUNET_malloc (sizeof (struct PendingMessage) +
981                      sizeof (struct RequestAddressMessage));
982   p->size = sizeof (struct RequestAddressMessage);
983   p->is_init = GNUNET_NO;
984   m = (struct RequestAddressMessage *) &p[1];
985   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
986   m->header.size = htons (sizeof (struct RequestAddressMessage));
987   m->reserved = htonl (0);
988   m->peer = *peer;
989   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
990   do_transmit (sh);
991 }
992
993 /**
994  * We have updated performance statistics for a given address.  Note
995  * that this function can be called for addresses that are currently
996  * in use as well as addresses that are valid but not actively in use.
997  * Furthermore, the peer may not even be connected to us right now (in
998  * which case the call may be ignored or the information may be stored
999  * for later use).  Update bandwidth assignments.
1000  *
1001  * @param sh handle
1002  * @param address the address
1003  * @param session session handle (if available)
1004  * @param ats performance data for the address
1005  * @param ats_count number of performance records in 'ats'
1006  */
1007 void
1008 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1009                            const struct GNUNET_HELLO_Address *address,
1010                            struct Session *session,
1011                            const struct GNUNET_ATS_Information *ats,
1012                            uint32_t ats_count)
1013 {
1014   struct PendingMessage *p;
1015   struct AddressUpdateMessage *m;
1016   struct GNUNET_ATS_Information *am;
1017   char *pm;
1018   size_t namelen;
1019   size_t msize;
1020
1021   if ((address == NULL) && (session == NULL))
1022   {
1023     GNUNET_break (0);
1024   }
1025
1026   namelen =
1027       (address->transport_name ==
1028        NULL) ? 0 : strlen (address->transport_name) + 1;
1029   msize =
1030       sizeof (struct AddressUpdateMessage) + address->address_length +
1031       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1032   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1033       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1034       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1035       (ats_count >=
1036        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1037   {
1038     GNUNET_break (0);
1039     return;
1040   }
1041
1042   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1043   p->size = msize;
1044   p->is_init = GNUNET_NO;
1045   m = (struct AddressUpdateMessage *) &p[1];
1046   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1047   m->header.size = htons (msize);
1048   m->ats_count = htonl (ats_count);
1049   m->peer = address->peer;
1050   m->address_length = htons (address->address_length);
1051   m->plugin_name_length = htons (namelen);
1052   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1053   am = (struct GNUNET_ATS_Information *) &m[1];
1054   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1055   pm = (char *) &am[ats_count];
1056   memcpy (pm, address->address, address->address_length);
1057   memcpy (&pm[address->address_length], address->transport_name, namelen);
1058   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1059   do_transmit (sh);
1060 }
1061
1062
1063 /**
1064  * An address is now in use or not used any more.
1065  *
1066  * @param sh handle
1067  * @param address the address
1068  * @param session session handle
1069  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
1070  * if address is not used any more
1071  */
1072 void
1073 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1074                            const struct GNUNET_HELLO_Address *address,
1075                            struct Session *session, int in_use)
1076 {
1077   struct PendingMessage *p;
1078   struct AddressUseMessage *m;
1079   char *pm;
1080   size_t namelen;
1081   size_t msize;
1082
1083   GNUNET_assert (NULL != address);
1084   namelen =
1085       (address->transport_name ==
1086        NULL) ? 0 : strlen (address->transport_name) + 1;
1087   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
1088   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1089       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1090       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1091   {
1092     GNUNET_break (0);
1093     return;
1094   }
1095
1096   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1097   p->size = msize;
1098   p->is_init = GNUNET_NO;
1099   m = (struct AddressUseMessage *) &p[1];
1100   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1101   m->header.size = htons (msize);
1102   m->peer = address->peer;
1103   m->in_use = htons (in_use);
1104   m->address_length = htons (address->address_length);
1105   m->plugin_name_length = htons (namelen);
1106   m->session_id = htonl (get_session_id (sh, session, &address->peer));
1107   pm = (char *) &m[1];
1108   memcpy (pm, address->address, address->address_length);
1109   memcpy (&pm[address->address_length], address->transport_name, namelen);
1110   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1111
1112   do_transmit (sh);
1113 }
1114
1115 /**
1116  * A session got destroyed, stop including it as a valid address.
1117  *
1118  * @param sh handle
1119  * @param address the address
1120  * @param session session handle that is no longer valid
1121  */
1122 void
1123 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1124                               const struct GNUNET_HELLO_Address *address,
1125                               struct Session *session)
1126 {
1127   struct PendingMessage *p;
1128   struct AddressDestroyedMessage *m;
1129   char *pm;
1130   size_t namelen;
1131   size_t msize;
1132   uint32_t session_id;
1133
1134   GNUNET_assert (address->transport_name != NULL);
1135   namelen = strlen (address->transport_name) + 1;
1136   GNUNET_assert (namelen > 1);
1137   msize =
1138       sizeof (struct AddressDestroyedMessage) + address->address_length +
1139       namelen;
1140   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1141       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1142       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1143   {
1144     GNUNET_break (0);
1145     return;
1146   }
1147
1148   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1149   p->size = msize;
1150   p->is_init = GNUNET_NO;
1151   m = (struct AddressDestroyedMessage *) &p[1];
1152   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1153   m->header.size = htons (msize);
1154   m->reserved = htonl (0);
1155   m->peer = address->peer;
1156   m->address_length = htons (address->address_length);
1157   m->plugin_name_length = htons (namelen);
1158   session_id = get_session_id (sh, session, &address->peer);
1159   m->session_id = htonl (session_id);
1160   pm = (char *) &m[1];
1161   memcpy (pm, address->address, address->address_length);
1162   memcpy (&pm[address->address_length], address->transport_name, namelen);
1163   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1164   do_transmit (sh);
1165   remove_session (sh, session_id, &address->peer);
1166 }
1167
1168 /* end of ats_api_scheduling.c */