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