enabling use of pipes for signal communication also on UNIX to enable future integrat...
[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 = GNUNET_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 sh the scheduling handle
758  * @param addr address
759  * @param addrlen address length
760  * @return location as GNUNET_ATS_Information
761  */
762
763 const struct GNUNET_ATS_Information
764 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
765 {
766   GNUNET_assert (sh != NULL);
767   struct GNUNET_ATS_Information ats;
768   struct ATS_Network * cur = sh->net_head;
769   int type = GNUNET_ATS_NET_UNSPECIFIED;
770
771   if  (addr->sa_family == AF_UNIX)
772   {
773     type = GNUNET_ATS_NET_LOOPBACK;
774   }
775
776   /* IPv4 loopback check */
777   if  (addr->sa_family == AF_INET)
778   {
779     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
780
781     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
782       type = GNUNET_ATS_NET_LOOPBACK;
783   }
784   /* IPv6 loopback check */
785   if  (addr->sa_family == AF_INET6)
786   {
787     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
788     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
789       type = GNUNET_ATS_NET_LOOPBACK;
790   }
791
792   /* Check local networks */
793   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
794   {
795     if (addrlen != cur->length)
796     {
797       cur = cur->next;
798       continue;
799     }
800
801     if (addr->sa_family == AF_INET)
802     {
803       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
804       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
805       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
806
807       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
808       {
809         char * net = GNUNET_strdup (GNUNET_a2s ((const struct sockaddr *) net4, addrlen));
810         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' is in network `%s'\n",
811             GNUNET_a2s ((const struct sockaddr *)a4, addrlen),
812             net);
813         GNUNET_free (net);
814         type = GNUNET_ATS_NET_LAN;
815       }
816     }
817     if (addr->sa_family == AF_INET6)
818     {
819       struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
820       struct sockaddr_in6 * net6 = (struct sockaddr_in6 *) cur->network;
821       struct sockaddr_in6 * mask6 = (struct sockaddr_in6 *) cur->netmask;
822
823       int res = GNUNET_YES;
824       int c = 0;
825       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
826       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
827       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
828       for (c = 0; c < 4; c++)
829         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
830           res = GNUNET_NO;
831
832       if (res == GNUNET_YES)
833       {
834         char * net = GNUNET_strdup (GNUNET_a2s ((const struct sockaddr *) net6, addrlen));
835         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' is in network `%s'\n",
836               GNUNET_a2s ((const struct sockaddr *) a6, addrlen),
837               net);
838         GNUNET_free (net);
839         type = GNUNET_ATS_NET_LAN;
840       }
841     }
842     cur = cur->next;
843   }
844
845   /* no local network found for this address, default: WAN */
846   if (type == GNUNET_ATS_NET_UNSPECIFIED)
847     type = GNUNET_ATS_NET_WAN;
848
849 #if VERBOSE
850   const char * range;
851   switch (type) {
852     case GNUNET_ATS_NET_WAN:
853         range = "WAN";
854       break;
855     case GNUNET_ATS_NET_LAN:
856         range = "LAN";
857       break;
858     case GNUNET_ATS_NET_LOOPBACK:
859         range = "LOOPBACK";
860       break;
861     default:
862
863       break;
864   }
865   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "`%s' is in network `%s'\n",
866         GNUNET_a2s ((const struct sockaddr *) addr, addrlen),
867         range);
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 */