use -Wl on -no-undefined as it is a linker option:
[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
31 #define INTERFACE_PROCESSING_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
32
33 #define NOT_FOUND 0
34
35 /**
36  * Message in linked list we should send to the ATS service.  The
37  * actual binary message follows this struct.
38  */
39 struct PendingMessage
40 {
41
42   /**
43    * Kept in a DLL.
44    */
45   struct PendingMessage *next;
46
47   /**
48    * Kept in a DLL.
49    */
50   struct PendingMessage *prev;
51
52   /**
53    * Size of the message.
54    */
55   size_t size;
56
57   /**
58    * Is this the 'ATS_START' message?
59    */
60   int is_init;
61 };
62
63
64 /**
65  * Information we track per session.
66  */
67 struct SessionRecord
68 {
69   /**
70    * Identity of the peer (just needed for error checking).
71    */
72   struct GNUNET_PeerIdentity peer;
73
74   /**
75    * Session handle.
76    */
77   struct Session *session;
78
79   /**
80    * Set to GNUNET_YES if the slot is used.
81    */
82   int slot_used;
83 };
84
85
86 struct ATS_Network
87 {
88   struct ATS_Network * next;
89
90   struct ATS_Network * prev;
91
92   struct sockaddr *network;
93   struct sockaddr *netmask;
94   socklen_t length;
95 };
96
97 /**
98  * Handle for address suggestions
99  */
100 struct GNUNET_ATS_SuggestHandle
101 {
102   struct GNUNET_ATS_SuggestHandle *prev;
103   struct GNUNET_ATS_SuggestHandle *next;
104   struct GNUNET_PeerIdentity id;
105 };
106
107
108 /**
109  * Handle to the ATS subsystem for bandwidth/transport scheduling information.
110  */
111 struct GNUNET_ATS_SchedulingHandle
112 {
113
114   /**
115    * Our configuration.
116    */
117   const struct GNUNET_CONFIGURATION_Handle *cfg;
118
119   /**
120    * Callback to invoke on suggestions.
121    */
122   GNUNET_ATS_AddressSuggestionCallback suggest_cb;
123
124   /**
125    * Closure for 'suggest_cb'.
126    */
127   void *suggest_cb_cls;
128
129   /**
130    * DLL for suggestions head
131    */
132   struct GNUNET_ATS_SuggestHandle *sug_head;
133
134   /**
135    * DLL for suggestions tail
136    */
137   struct GNUNET_ATS_SuggestHandle *sug_tail;
138
139   /**
140    * Connection to ATS service.
141    */
142   struct GNUNET_CLIENT_Connection *client;
143
144   /**
145    * Head of list of messages for the ATS service.
146    */
147   struct PendingMessage *pending_head;
148
149   /**
150    * Tail of list of messages for the ATS service
151    */
152   struct PendingMessage *pending_tail;
153
154   /**
155    * Current request for transmission to ATS.
156    */
157   struct GNUNET_CLIENT_TransmitHandle *th;
158
159   /**
160    * Head of network list
161    */
162   struct ATS_Network * net_head;
163
164   /**
165    * Tail of network list
166    */
167   struct ATS_Network * net_tail;
168
169   /**
170    * Array of session objects (we need to translate them to numbers and back
171    * for the protocol; the offset in the array is the session number on the
172    * network).  Index 0 is always NULL and reserved to represent the NULL pointer.
173    * Unused entries are also NULL.
174    */
175   struct SessionRecord *session_array;
176
177   /**
178    * Task to trigger reconnect.
179    */
180   GNUNET_SCHEDULER_TaskIdentifier task;
181
182   /**
183    * Task retrieving interfaces from the system
184    */
185   GNUNET_SCHEDULER_TaskIdentifier interface_task;
186
187
188   /**
189    * Size of the session array.
190    */
191   unsigned int session_array_size;
192
193   /**
194    * Should we reconnect to ATS due to some serious error?
195    */
196   int reconnect;
197 };
198
199
200 /**
201  * Re-establish the connection to the ATS service.
202  *
203  * @param sh handle to use to re-connect.
204  */
205 static void
206 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
207
208
209 /**
210  * Re-establish the connection to the ATS service.
211  *
212  * @param cls handle to use to re-connect.
213  * @param tc scheduler context
214  */
215 static void
216 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
217 {
218   struct GNUNET_ATS_SchedulingHandle *sh = cls;
219
220   sh->task = GNUNET_SCHEDULER_NO_TASK;
221   reconnect (sh);
222 }
223
224
225 /**
226  * Disconnect from ATS and then reconnect.
227  *
228  * @param sh our handle
229  */
230 static void
231 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
232 {
233   sh->reconnect = GNUNET_NO;
234   GNUNET_CLIENT_disconnect (sh->client);
235   sh->client = NULL;
236   sh->task =
237       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
238                                     sh);
239 }
240
241
242 /**
243  * Transmit messages from the message queue to the service
244  * (if there are any, and if we are not already trying).
245  *
246  * @param sh handle to use
247  */
248 static void
249 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh);
250
251
252 /**
253  * Type of a function to call when we receive a message
254  * from the service.
255  *
256  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
257  * @param msg message received, NULL on timeout or fatal error
258  */
259 static void
260 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg);
261
262
263 /**
264  * We can now transmit a message to ATS. Do it.
265  *
266  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
267  * @param size number of bytes we can transmit to ATS
268  * @param buf where to copy the messages
269  * @return number of bytes copied into buf
270  */
271 static size_t
272 transmit_message_to_ats (void *cls, size_t size, void *buf)
273 {
274   struct GNUNET_ATS_SchedulingHandle *sh = cls;
275   struct PendingMessage *p;
276   size_t ret;
277   char *cbuf;
278
279   sh->th = NULL;
280   if ((size == 0) || (buf == NULL))
281   {
282     force_reconnect (sh);
283     return 0;
284   }
285   ret = 0;
286   cbuf = buf;
287   while ((NULL != (p = sh->pending_head)) && (p->size <= size))
288   {
289     memcpy (&cbuf[ret], &p[1], p->size);
290     ret += p->size;
291     size -= p->size;
292     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
293     GNUNET_free (p);
294   }
295   do_transmit (sh);
296   return ret;
297 }
298
299
300 /**
301  * Transmit messages from the message queue to the service
302  * (if there are any, and if we are not already trying).
303  *
304  * @param sh handle to use
305  */
306 static void
307 do_transmit (struct GNUNET_ATS_SchedulingHandle *sh)
308 {
309   struct PendingMessage *p;
310
311   if (NULL != sh->th)
312     return;
313   if (NULL == (p = sh->pending_head))
314     return;
315   if (NULL == sh->client)
316     return;                     /* currently reconnecting */
317   sh->th =
318       GNUNET_CLIENT_notify_transmit_ready (sh->client, p->size,
319                                            GNUNET_TIME_UNIT_FOREVER_REL,
320                                            GNUNET_NO, &transmit_message_to_ats,
321                                            sh);
322 }
323
324
325 /**
326  * Find the session object corresponding to the given session ID.
327  *
328  * @param sh our handle
329  * @param session_id current session ID
330  * @param peer peer the session belongs to
331  * @return the session object (or NULL)
332  */
333 static struct Session *
334 find_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
335               const struct GNUNET_PeerIdentity *peer)
336 {
337
338   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
339               "Find session %u from peer %s in %p\n",
340               (unsigned int) session_id, GNUNET_i2s (peer), sh);
341
342   if (session_id >= sh->session_array_size)
343   {
344     GNUNET_break (0);
345     return NULL;
346   }
347   if (0 == session_id)
348     return NULL;
349   if (sh->session_array[session_id].session == NULL)
350   {
351     GNUNET_break (0 ==
352                   memcmp (peer, &sh->session_array[session_id].peer,
353                           sizeof (struct GNUNET_PeerIdentity)));
354     return NULL;
355   }
356
357   if (0 !=
358       memcmp (peer, &sh->session_array[session_id].peer,
359               sizeof (struct GNUNET_PeerIdentity)))
360   {
361     GNUNET_break (0);
362     sh->reconnect = GNUNET_YES;
363     return NULL;
364   }
365   /* This check exploits the fact that first field of a session object
366    * is peer identity.
367    */
368   if (0 !=
369       memcmp (peer, sh->session_array[session_id].session,
370               sizeof (struct GNUNET_PeerIdentity)))
371   {
372     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
373               "Session %p belongs to peer `%s'\n",
374               sh->session_array[session_id].session, GNUNET_i2s_full ((struct GNUNET_PeerIdentity *) &sh->session_array[session_id].peer));
375 /*
376     GNUNET_break (0);
377     sh->reconnect = GNUNET_YES;
378     return NULL;
379 */
380   }
381   return sh->session_array[session_id].session;
382 }
383
384
385 /**
386  * Get an available session ID for the given session object.
387  *
388  * @param sh our handle
389  * @param session session object
390  * @param peer peer the session belongs to
391  * @return the session id
392  */
393 static uint32_t
394 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
395                 const struct GNUNET_PeerIdentity *peer)
396 {
397   unsigned int i;
398   unsigned int f;
399
400   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
401               "Get session ID for session %p from peer %s in %p\n", session,
402               GNUNET_i2s (peer), sh);
403
404   if (NULL == session)
405     return NOT_FOUND;
406   f = 0;
407   for (i = 1; i < sh->session_array_size; i++)
408   {
409     if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
410       f = i;
411   }
412   if (f == 0)
413   {
414     f = sh->session_array_size;
415     GNUNET_array_grow (sh->session_array, sh->session_array_size,
416                        sh->session_array_size * 2);
417   }
418   GNUNET_assert (f > 0);
419   sh->session_array[f].session = session;
420   sh->session_array[f].peer = *peer;
421   sh->session_array[f].slot_used = GNUNET_YES;
422
423   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
424               "Assigning session ID %u for session %p of peer %s in %p\n", f,
425               session, GNUNET_i2s (peer), sh);
426
427   return f;
428 }
429
430
431 /**
432  * Get the ID for the given session object.
433  *
434  * @param sh our handle
435  * @param session session object
436  * @param peer peer the session belongs to
437  * @return the session id or NOT_FOUND for error
438  */
439 static uint32_t
440 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh, struct Session *session,
441                 const struct GNUNET_PeerIdentity *peer)
442 {
443   unsigned int i;
444   char * p2;
445
446   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
447               "Get session ID for session %p from peer %s in %p\n", session,
448               GNUNET_i2s (peer), sh);
449
450   if (NULL == session)
451     return NOT_FOUND;
452   for (i = 1; i < sh->session_array_size; i++)
453   {
454     if (session == sh->session_array[i].session)
455     {
456       if (0 != memcmp (peer, &sh->session_array[i].peer,
457                        sizeof (struct GNUNET_PeerIdentity)))
458       {
459         p2 = strdup (GNUNET_i2s (&sh->session_array[i].peer));
460         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "ats-scheduling-api",
461                     "Session %p did not match: old session was for peer `%s' new session is for `%s'\n",
462                     session, GNUNET_i2s (peer), p2);
463         GNUNET_free (p2);
464         return NOT_FOUND;
465       }
466       return i;
467     }
468   }
469   return NOT_FOUND;
470 }
471
472
473 /**
474  * Remove the session of the given session ID from the session
475  * table (it is no longer valid).
476  *
477  * @param sh our handle
478  * @param session_id identifies session that is no longer valid
479  * @param peer peer the session belongs to
480  */
481 static void
482 remove_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
483                 const struct GNUNET_PeerIdentity *peer)
484 {
485   GNUNET_assert (peer != NULL);
486   GNUNET_assert (sh != NULL);
487
488   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
489               "Release sessionID %u from peer %s in %p\n",
490               (unsigned int) session_id, GNUNET_i2s (peer), sh);
491
492   if (0 == session_id)
493     return;
494
495   GNUNET_assert (session_id < sh->session_array_size);
496   GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
497   GNUNET_assert (0 == memcmp (peer,
498                               &sh->session_array[session_id].peer,
499                               sizeof (struct GNUNET_PeerIdentity)));
500   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
501               "Session %p for peer `%s' removed from slot %u \n",
502               sh->session_array[session_id].session,
503               GNUNET_i2s (peer),
504               session_id);
505   sh->session_array[session_id].session = NULL;
506
507 }
508
509
510 /**
511  * Release the session slot from the session table (ATS service is
512  * also done using it).
513  *
514  * @param sh our handle
515  * @param session_id identifies session that is no longer valid
516  * @param peer peer the session belongs to
517  */
518 static void
519 release_session (struct GNUNET_ATS_SchedulingHandle *sh, uint32_t session_id,
520                  const struct GNUNET_PeerIdentity *peer)
521 {
522
523   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
524               "Release sessionID %u from peer %s in %p\n",
525               (unsigned int) session_id, GNUNET_i2s (peer), sh);
526
527   if (session_id >= sh->session_array_size)
528   {
529     GNUNET_break (0);
530     sh->reconnect = GNUNET_YES;
531     return;
532   }
533
534   /* this slot should have been removed from remove_session before */
535   GNUNET_assert (sh->session_array[session_id].session == NULL);
536
537   if (0 !=
538       memcmp (peer, &sh->session_array[session_id].peer,
539               sizeof (struct GNUNET_PeerIdentity)))
540   {
541     GNUNET_break (0);
542     sh->reconnect = GNUNET_YES;
543     return;
544   }
545   sh->session_array[session_id].slot_used = GNUNET_NO;
546   memset (&sh->session_array[session_id].peer, 0,
547           sizeof (struct GNUNET_PeerIdentity));
548 }
549
550
551 static void
552 process_release_message (struct GNUNET_ATS_SchedulingHandle *sh,
553                          const struct SessionReleaseMessage *srm)
554 {
555   release_session (sh, ntohl (srm->session_id), &srm->peer);
556 }
557
558
559 /**
560  * Type of a function to call when we receive a message
561  * from the service.
562  *
563  * @param cls the 'struct GNUNET_ATS_SchedulingHandle'
564  * @param msg message received, NULL on timeout or fatal error
565  */
566 static void
567 process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg)
568 {
569   struct GNUNET_ATS_SchedulingHandle *sh = cls;
570   const struct AddressSuggestionMessage *m;
571   const struct GNUNET_ATS_Information *atsi;
572   const char *plugin_address;
573   const char *plugin_name;
574   uint16_t plugin_address_length;
575   uint16_t plugin_name_length;
576   uint32_t ats_count;
577   struct GNUNET_HELLO_Address address;
578   struct Session *s;
579
580   if (NULL == msg)
581   {
582     force_reconnect (sh);
583     return;
584   }
585   if ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE) &&
586       (ntohs (msg->size) == sizeof (struct SessionReleaseMessage)))
587   {
588     process_release_message (sh, (const struct SessionReleaseMessage *) msg);
589     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
590                            GNUNET_TIME_UNIT_FOREVER_REL);
591     if (GNUNET_YES == sh->reconnect)
592       force_reconnect (sh);
593     return;
594   }
595   if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION) ||
596       (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage)))
597   {
598     GNUNET_break (0);
599     force_reconnect (sh);
600     return;
601   }
602   m = (const struct AddressSuggestionMessage *) msg;
603   ats_count = ntohl (m->ats_count);
604   plugin_address_length = ntohs (m->address_length);
605   atsi = (const struct GNUNET_ATS_Information *) &m[1];
606   plugin_address = (const char *) &atsi[ats_count];
607   plugin_name = &plugin_address[plugin_address_length];
608   plugin_name_length = ntohs (m->plugin_name_length);
609   if ((plugin_address_length + plugin_name_length +
610        ats_count * sizeof (struct GNUNET_ATS_Information) +
611        sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
612       (ats_count >
613        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
614       || (plugin_name[plugin_name_length - 1] != '\0'))
615   {
616     GNUNET_break (0);
617     force_reconnect (sh);
618     return;
619   }
620   uint32_t session_id = ntohl (m->session_id);
621
622   if (session_id == 0)
623     s = NULL;
624   else
625   {
626     s = find_session (sh, session_id, &m->peer);
627     if (s == NULL)
628     {
629
630       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
631                   "ATS tries to use outdated session `%s'\n",
632                   GNUNET_i2s (&m->peer));
633       GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
634                              GNUNET_TIME_UNIT_FOREVER_REL);
635       return;
636     }
637   }
638
639   if (NULL == sh->suggest_cb)
640         return;
641
642   address.peer = m->peer;
643   address.address = plugin_address;
644   address.address_length = plugin_address_length;
645   address.transport_name = plugin_name;
646   address.local_info = ntohl(m->address_local_info);
647
648   if ((s == NULL) && (0 == address.address_length))
649   {
650     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651                 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
652                 GNUNET_i2s (&address.peer), address.transport_name,
653                 plugin_address_length, session_id);
654     GNUNET_break_op (0);
655     GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
656                            GNUNET_TIME_UNIT_FOREVER_REL);
657     return;
658   }
659
660   sh->suggest_cb (sh->suggest_cb_cls,
661                   (const struct GNUNET_PeerIdentity *) &m->peer,
662                   &address, s, m->bandwidth_out,
663                   m->bandwidth_in, atsi, ats_count);
664
665   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
666                          GNUNET_TIME_UNIT_FOREVER_REL);
667   if (GNUNET_YES == sh->reconnect)
668     force_reconnect (sh);
669 }
670
671
672 /**
673  * Re-establish the connection to the ATS service.
674  *
675  * @param sh handle to use to re-connect.
676  */
677 static void
678 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
679 {
680   struct PendingMessage *p;
681   struct ClientStartMessage *init;
682
683   GNUNET_assert (NULL == sh->client);
684   sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
685   GNUNET_assert (NULL != sh->client);
686   GNUNET_CLIENT_receive (sh->client, &process_ats_message, sh,
687                            GNUNET_TIME_UNIT_FOREVER_REL);
688   if ((NULL == (p = sh->pending_head)) || (GNUNET_YES != p->is_init))
689   {
690     p = GNUNET_malloc (sizeof (struct PendingMessage) +
691                        sizeof (struct ClientStartMessage));
692     p->size = sizeof (struct ClientStartMessage);
693     p->is_init = GNUNET_YES;
694     init = (struct ClientStartMessage *) &p[1];
695     init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START);
696     init->header.size = htons (sizeof (struct ClientStartMessage));
697     init->start_flag = htonl (START_FLAG_SCHEDULING);
698     GNUNET_CONTAINER_DLL_insert (sh->pending_head, sh->pending_tail, p);
699   }
700   do_transmit (sh);
701 }
702
703
704 /**
705  * delete the current network list
706  */
707 static void
708 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
709 {
710   struct ATS_Network * cur = sh->net_head;
711   while (cur != NULL)
712   {
713     GNUNET_CONTAINER_DLL_remove(sh->net_head, sh->net_tail, cur);
714     GNUNET_free (cur);
715     cur = sh->net_head;
716   }
717 }
718
719
720 static int
721 interface_proc (void *cls, const char *name,
722                 int isDefault,
723                 const struct sockaddr *
724                 addr,
725                 const struct sockaddr *
726                 broadcast_addr,
727                 const struct sockaddr *
728                 netmask, socklen_t addrlen)
729 {
730   struct GNUNET_ATS_SchedulingHandle * sh = cls;
731   /* Calculate network */
732   struct ATS_Network *net = NULL;
733
734   /* Skipping IPv4 loopback addresses since we have special check  */
735   if  (addr->sa_family == AF_INET)
736   {
737     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
738
739     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
740        return GNUNET_OK;
741   }
742   /* Skipping IPv6 loopback addresses since we have special check  */
743   if  (addr->sa_family == AF_INET6)
744   {
745     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
746     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
747       return GNUNET_OK;
748   }
749
750   if (addr->sa_family == AF_INET)
751   {
752     struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
753     struct sockaddr_in *netmask4 = (struct sockaddr_in *) netmask;
754     struct sockaddr_in *tmp = NULL;
755     struct sockaddr_in network4;
756
757     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
758     tmp = (struct sockaddr_in *) &net[1];
759     net->network = (struct sockaddr *) &tmp[0];
760     net->netmask = (struct sockaddr *) &tmp[1];
761     net->length = addrlen;
762
763     memset (&network4, 0, sizeof (network4));
764     network4.sin_family = AF_INET;
765 #if HAVE_SOCKADDR_IN_SIN_LEN
766     network4.sin_len = sizeof (network4);
767 #endif
768     network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
769
770     memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
771     memcpy (net->network, &network4, sizeof (struct sockaddr_in));
772   }
773
774   if (addr->sa_family == AF_INET6)
775   {
776     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
777     struct sockaddr_in6 *netmask6 = (struct sockaddr_in6 *) netmask;
778     struct sockaddr_in6 * tmp = NULL;
779     struct sockaddr_in6 network6;
780
781     net = GNUNET_malloc(sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
782     tmp = (struct sockaddr_in6 *) &net[1];
783     net->network = (struct sockaddr *) &tmp[0];
784     net->netmask = (struct sockaddr *) &tmp[1];
785     net->length = addrlen;
786
787     memset (&network6, 0, sizeof (network6));
788     network6.sin6_family = AF_INET6;
789 #if HAVE_SOCKADDR_IN_SIN_LEN
790     network6.sin6_len = sizeof (network6);
791 #endif
792     int c = 0;
793     uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
794     uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
795     uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
796     for (c = 0; c < 4; c++)
797       net_elem[c] = addr_elem[c] & mask_elem[c];
798
799     memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
800     memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
801   }
802
803   /* Store in list */
804   if (net != NULL)
805   {
806 #if VERBOSE_ATS
807     char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
808     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding network `%s', netmask `%s'\n",
809         GNUNET_a2s((struct sockaddr *) net->network, addrlen),
810         netmask);
811     GNUNET_free (netmask);
812 # endif
813     GNUNET_CONTAINER_DLL_insert(sh->net_head, sh->net_tail, net);
814   }
815   return GNUNET_OK;
816 }
817
818
819 /**
820  * Periodically get list of addresses
821  * @param cls closure
822  * @param tc Task context
823  */
824 static void
825 get_addresses (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
826 {
827   struct GNUNET_ATS_SchedulingHandle * sh = cls;
828   sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
829   delete_networks (sh);
830   GNUNET_OS_network_interfaces_list(interface_proc, sh);
831   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
832                                                      get_addresses,
833                                                      sh);
834 }
835
836 /**
837  * Convert a GNUNET_ATS_NetworkType to a string
838  *
839  * @param net the network type
840  * @return a string or NULL if invalid
841  */
842 const char *
843 GNUNET_ATS_print_network_type (uint32_t net)
844 {
845   char *networks[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
846   if (net < GNUNET_ATS_NetworkTypeCount)
847     return networks[net];
848   return NULL;
849 }
850
851 /**
852  * Convert a ATS property to a string
853  *
854  * @param type the atsi type
855  * @return a string or NULL if invalid
856  */
857 const char *
858 GNUNET_ATS_print_property_type (uint32_t type)
859 {
860         char *props[GNUNET_ATS_PropertyCount] = GNUNET_ATS_PropertyStrings;
861         if ((type > 0) && (type < GNUNET_ATS_PropertyCount))
862                 return props[type];
863         return NULL;
864 }
865
866
867 /**
868  * Returns where the address is located: LAN or WAN or ...
869  *
870  * @param sh the scheduling handle
871  * @param addr address
872  * @param addrlen address length
873  * @return location as GNUNET_ATS_Information
874  */
875 struct GNUNET_ATS_Information
876 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle * sh, const struct sockaddr * addr, socklen_t addrlen)
877 {
878   GNUNET_assert (sh != NULL);
879   struct ATS_Network * cur = sh->net_head;
880
881   int type = GNUNET_ATS_NET_UNSPECIFIED;
882   struct GNUNET_ATS_Information ats;
883
884   if  (addr->sa_family == AF_UNIX)
885   {
886     type = GNUNET_ATS_NET_LOOPBACK;
887   }
888
889   /* IPv4 loopback check */
890   if  (addr->sa_family == AF_INET)
891   {
892     struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
893
894     if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
895       type = GNUNET_ATS_NET_LOOPBACK;
896   }
897   /* IPv6 loopback check */
898   if  (addr->sa_family == AF_INET6)
899   {
900     struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
901     if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
902       type = GNUNET_ATS_NET_LOOPBACK;
903   }
904
905   /* Check local networks */
906   while ((cur != NULL) && (type == GNUNET_ATS_NET_UNSPECIFIED))
907   {
908     if (addrlen != cur->length)
909     {
910       cur = cur->next;
911       continue;
912     }
913
914     if (addr->sa_family == AF_INET)
915     {
916       struct sockaddr_in * a4 = (struct sockaddr_in *) addr;
917       struct sockaddr_in * net4 = (struct sockaddr_in *) cur->network;
918       struct sockaddr_in * mask4 = (struct sockaddr_in *) cur->netmask;
919
920       if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
921         type = GNUNET_ATS_NET_LAN;
922     }
923     if (addr->sa_family == AF_INET6)
924     {
925       struct sockaddr_in6 * a6 = (struct sockaddr_in6 *) addr;
926       struct sockaddr_in6 * net6 = (struct sockaddr_in6 *) cur->network;
927       struct sockaddr_in6 * mask6 = (struct sockaddr_in6 *) cur->netmask;
928
929       int res = GNUNET_YES;
930       int c = 0;
931       uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
932       uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
933       uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
934       for (c = 0; c < 4; c++)
935         if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
936           res = GNUNET_NO;
937
938       if (res == GNUNET_YES)
939         type = GNUNET_ATS_NET_LAN;
940     }
941     cur = cur->next;
942   }
943
944   /* no local network found for this address, default: WAN */
945   if (type == GNUNET_ATS_NET_UNSPECIFIED)
946     type = GNUNET_ATS_NET_WAN;
947   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
948   ats.value = htonl (type);
949
950   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
951                    "`%s' is in network `%s'\n",
952                    GNUNET_a2s ((const struct sockaddr *) addr, addrlen),
953                    GNUNET_ATS_print_network_type(type));
954   return ats;
955 }
956
957
958 /**
959  * Initialize the ATS subsystem.
960  *
961  * @param cfg configuration to use
962  * @param suggest_cb notification to call whenever the suggestation changed
963  * @param suggest_cb_cls closure for 'suggest_cb'
964  * @return ats context
965  */
966 struct GNUNET_ATS_SchedulingHandle *
967 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
968                             GNUNET_ATS_AddressSuggestionCallback suggest_cb,
969                             void *suggest_cb_cls)
970 {
971   struct GNUNET_ATS_SchedulingHandle *sh;
972
973   sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
974   sh->cfg = cfg;
975   sh->suggest_cb = suggest_cb;
976   sh->suggest_cb_cls = suggest_cb_cls;
977   GNUNET_array_grow (sh->session_array, sh->session_array_size, 4);
978   GNUNET_OS_network_interfaces_list(interface_proc, sh);
979   sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVALL,
980       get_addresses,
981       sh);
982   reconnect (sh);
983   return sh;
984 }
985
986
987 /**
988  * Client is done with ATS scheduling, release resources.
989  *
990  * @param sh handle to release
991  */
992 void
993 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
994 {
995   struct PendingMessage *p;
996   struct GNUNET_ATS_SuggestHandle *cur;
997   struct GNUNET_ATS_SuggestHandle *next;
998   while (NULL != (p = sh->pending_head))
999   {
1000     GNUNET_CONTAINER_DLL_remove (sh->pending_head, sh->pending_tail, p);
1001     GNUNET_free (p);
1002   }
1003   if (NULL != sh->client)
1004   {
1005     GNUNET_CLIENT_disconnect (sh->client);
1006     sh->client = NULL;
1007   }
1008   if (GNUNET_SCHEDULER_NO_TASK != sh->task)
1009   {
1010     GNUNET_SCHEDULER_cancel (sh->task);
1011     sh->task = GNUNET_SCHEDULER_NO_TASK;
1012   }
1013
1014   next = sh->sug_head;
1015   while (NULL != (cur = next))
1016   {
1017                 next = cur->next;
1018                 GNUNET_CONTAINER_DLL_remove (sh->sug_head, sh->sug_tail, cur);
1019                 GNUNET_free (cur);
1020   }
1021
1022   delete_networks (sh);
1023   if (sh->interface_task != GNUNET_SCHEDULER_NO_TASK)
1024   {
1025     GNUNET_SCHEDULER_cancel(sh->interface_task);
1026     sh->interface_task = GNUNET_SCHEDULER_NO_TASK;
1027   }
1028   GNUNET_array_grow (sh->session_array, sh->session_array_size, 0);
1029   GNUNET_free (sh);
1030   sh = NULL;
1031 }
1032
1033 /**
1034  * We would like to reset the address suggestion block time for this
1035  * peer
1036  *
1037  * @param sh handle
1038  * @param peer identity of the peer we want to reset
1039  */
1040 void
1041 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1042                           const struct GNUNET_PeerIdentity *peer)
1043 {
1044   struct PendingMessage *p;
1045   struct ResetBackoffMessage *m;
1046
1047   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1048                      sizeof (struct ResetBackoffMessage));
1049   p->size = sizeof (struct ResetBackoffMessage);
1050   p->is_init = GNUNET_NO;
1051   m = (struct ResetBackoffMessage *) &p[1];
1052   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1053   m->header.size = htons (sizeof (struct ResetBackoffMessage));
1054   m->reserved = htonl (0);
1055   m->peer = *peer;
1056   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1057   do_transmit (sh);
1058 }
1059
1060 /**
1061  * We would like to receive address suggestions for a peer. ATS will
1062  * respond with a call to the continuation immediately containing an address or
1063  * no address if none is available. ATS can suggest more addresses until we call
1064  * #GNUNET_ATS_suggest_address_cancel.
1065  *
1066  *
1067  * @param sh handle
1068  * @param peer identity of the peer we need an address for
1069  * @param cont the continuation to call with the address
1070  * @param cont_cls the cls for the continuation
1071  * @return suggest handle
1072  */
1073 struct GNUNET_ATS_SuggestHandle *
1074 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1075                             const struct GNUNET_PeerIdentity *peer,
1076                             GNUNET_ATS_AddressSuggestionCallback cont,
1077                             void *cont_cls)
1078 {
1079   struct PendingMessage *p;
1080   struct RequestAddressMessage *m;
1081   struct GNUNET_ATS_SuggestHandle *s;
1082
1083   // FIXME: ATS needs to remember this in case of
1084   // a disconnect!
1085   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1086                      sizeof (struct RequestAddressMessage));
1087   p->size = sizeof (struct RequestAddressMessage);
1088   p->is_init = GNUNET_NO;
1089   m = (struct RequestAddressMessage *) &p[1];
1090   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1091   m->header.size = htons (sizeof (struct RequestAddressMessage));
1092   m->reserved = htonl (0);
1093   m->peer = *peer;
1094   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1095   do_transmit (sh);
1096   s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1097   s->id = (*peer);
1098   GNUNET_CONTAINER_DLL_insert_tail (sh->sug_head, sh->sug_tail, s);
1099   return s;
1100 }
1101
1102
1103 /**
1104  * We would like to stop receiving address updates for this peer
1105  *
1106  * @param sh handle
1107  * @param peer identity of the peer
1108  */
1109 void
1110 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1111                                    const struct GNUNET_PeerIdentity *peer)
1112 {
1113   struct PendingMessage *p;
1114   struct RequestAddressMessage *m;
1115   struct GNUNET_ATS_SuggestHandle *s;
1116
1117   for (s = sh->sug_head; NULL != s; s = s->next)
1118         if (0 == memcmp(peer, &s->id, sizeof (s->id)))
1119                 break;
1120   if (NULL == s)
1121   {
1122         GNUNET_break (0);
1123         return;
1124   }
1125   else
1126   {
1127         GNUNET_CONTAINER_DLL_remove (sh->sug_head, sh->sug_tail, s);
1128         GNUNET_free (s);
1129   }
1130
1131   p = GNUNET_malloc (sizeof (struct PendingMessage) +
1132                      sizeof (struct RequestAddressMessage));
1133   p->size = sizeof (struct RequestAddressMessage);
1134   p->is_init = GNUNET_NO;
1135   m = (struct RequestAddressMessage *) &p[1];
1136   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1137   m->header.size = htons (sizeof (struct RequestAddressMessage));
1138   m->reserved = htonl (0);
1139   m->peer = *peer;
1140   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1141   do_transmit (sh);
1142 }
1143
1144
1145 /**
1146  * Test if a address and a session is known to ATS
1147  *
1148  * @param sh the scheduling handle
1149  * @param address the address
1150  * @param session the session
1151  * @return GNUNET_YES or GNUNET_NO
1152  */
1153 int
1154 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1155                                                                                         const struct GNUNET_HELLO_Address *address,
1156                                                                                         struct Session *session)
1157 {
1158         int s;
1159   if (NULL != session)
1160   {
1161     if (NOT_FOUND != (s = find_session_id (sh, session, &address->peer)))
1162     {
1163       /* Existing */
1164       return GNUNET_YES;
1165     }
1166     return GNUNET_NO;
1167   }
1168   return GNUNET_NO;
1169 }
1170
1171 /**
1172  * We have a new address ATS should know. Addresses have to be added with this
1173  * function before they can be: updated, set in use and destroyed
1174  *
1175  * @param sh handle
1176  * @param address the address
1177  * @param session session handle, can be NULL
1178  * @param ats performance data for the address
1179  * @param ats_count number of performance records in 'ats'
1180  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1181  */
1182 int
1183 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1184                         const struct GNUNET_HELLO_Address *address,
1185                         struct Session *session,
1186                         const struct GNUNET_ATS_Information *ats,
1187                         uint32_t ats_count)
1188 {
1189
1190   struct PendingMessage *p;
1191   struct AddressUpdateMessage *m;
1192   struct GNUNET_ATS_Information *am;
1193   char *pm;
1194   size_t namelen;
1195   size_t msize;
1196   uint32_t s = 0;
1197
1198   if (address == NULL)
1199   {
1200     GNUNET_break (0);
1201     return GNUNET_SYSERR;
1202   }
1203
1204   namelen = (address->transport_name == NULL) ? 0 : strlen (address->transport_name) + 1;
1205
1206   msize = sizeof (struct AddressUpdateMessage) + address->address_length +
1207       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1208   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1209       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1210       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1211       (ats_count >=
1212        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1213   {
1214     GNUNET_break (0);
1215     return GNUNET_SYSERR;
1216   }
1217
1218   if (NULL != session)
1219   {
1220     if (NOT_FOUND != (s = find_session_id (sh, session, &address->peer)))
1221     {
1222       /* Already existing, nothing todo */
1223       return GNUNET_SYSERR;
1224     }
1225     s = find_empty_session_slot (sh, session, &address->peer);
1226     GNUNET_break (NOT_FOUND != s);
1227   }
1228
1229   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1230   p->size = msize;
1231   p->is_init = GNUNET_NO;
1232   m = (struct AddressUpdateMessage *) &p[1];
1233   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1234   m->header.size = htons (msize);
1235   m->ats_count = htonl (ats_count);
1236   m->peer = address->peer;
1237   m->address_length = htons (address->address_length);
1238   m->address_local_info = htonl ((uint32_t) address->local_info);
1239   m->plugin_name_length = htons (namelen);
1240   m->session_id = htonl (s);
1241
1242   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1243               "Adding address for peer `%s', plugin `%s', session %p id %u\n",
1244               GNUNET_i2s (&address->peer),
1245               address->transport_name, session, s);
1246
1247   am = (struct GNUNET_ATS_Information *) &m[1];
1248   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1249   pm = (char *) &am[ats_count];
1250   memcpy (pm, address->address, address->address_length);
1251   if (NULL != address->transport_name)
1252         memcpy (&pm[address->address_length], address->transport_name, namelen);
1253   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1254   do_transmit (sh);
1255   return GNUNET_OK;
1256
1257 }
1258
1259
1260 /**
1261  * We have updated performance statistics for a given address.  Note
1262  * that this function can be called for addresses that are currently
1263  * in use as well as addresses that are valid but not actively in use.
1264  * Furthermore, the peer may not even be connected to us right now (in
1265  * which case the call may be ignored or the information may be stored
1266  * for later use).  Update bandwidth assignments.
1267  *
1268  * @param sh handle
1269  * @param address the address
1270  * @param session session handle, can be NULL
1271  * @param ats performance data for the address
1272  * @param ats_count number of performance records in 'ats'
1273  * @return GNUNET_YES on success, GNUNET_NO if address or session are unknown,
1274  * GNUNET_SYSERR on hard failure
1275  */
1276 int
1277 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1278                            const struct GNUNET_HELLO_Address *address,
1279                            struct Session *session,
1280                            const struct GNUNET_ATS_Information *ats,
1281                            uint32_t ats_count)
1282 {
1283   struct PendingMessage *p;
1284   struct AddressUpdateMessage *m;
1285   struct GNUNET_ATS_Information *am;
1286   char *pm;
1287   size_t namelen;
1288   size_t msize;
1289   uint32_t s = 0;
1290
1291   if (NULL == address)
1292   {
1293     GNUNET_break (0);
1294     return GNUNET_SYSERR;
1295   }
1296   if (NULL == sh)
1297   {
1298     GNUNET_break (0);
1299     return GNUNET_SYSERR;
1300   }
1301
1302   namelen = (address->transport_name ==
1303        NULL) ? 0 : strlen (address->transport_name) + 1;
1304   msize =
1305       sizeof (struct AddressUpdateMessage) + address->address_length +
1306       ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1307   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1308       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1309       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1310       (ats_count >=
1311        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1312   {
1313     GNUNET_break (0);
1314     return GNUNET_SYSERR;
1315   }
1316
1317   if (NULL != session)
1318   {
1319     s = find_session_id (sh, session, &address->peer);
1320     if (NOT_FOUND == s)
1321       return GNUNET_NO;
1322   }
1323
1324   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1325   p->size = msize;
1326   p->is_init = GNUNET_NO;
1327   m = (struct AddressUpdateMessage *) &p[1];
1328   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1329   m->header.size = htons (msize);
1330   m->ats_count = htonl (ats_count);
1331   m->peer = address->peer;
1332   m->address_length = htons (address->address_length);
1333   m->address_local_info = htonl ((uint32_t) address->local_info);
1334   m->plugin_name_length = htons (namelen);
1335
1336   m->session_id = htonl (s);
1337
1338   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1339               "Updating address for peer `%s', plugin `%s', session %p id %u\n",
1340               GNUNET_i2s (&address->peer),
1341               address->transport_name, session, s);
1342
1343   am = (struct GNUNET_ATS_Information *) &m[1];
1344   memcpy (am, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1345   pm = (char *) &am[ats_count];
1346   memcpy (pm, address->address, address->address_length);
1347   memcpy (&pm[address->address_length], address->transport_name, namelen);
1348   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1349   do_transmit (sh);
1350   return GNUNET_YES;
1351 }
1352
1353
1354 /**
1355  * An address is now in use or not used any more.
1356  *
1357  * @param sh handle
1358  * @param address the address
1359  * @param session session handle, can be NULL
1360  * @param in_use GNUNET_YES if this address is now used, GNUNET_NO
1361  * if address is not used any more
1362  */
1363 void
1364 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1365                            const struct GNUNET_HELLO_Address *address,
1366                            struct Session *session, int in_use)
1367 {
1368   struct PendingMessage *p;
1369   struct AddressUseMessage *m;
1370   char *pm;
1371   size_t namelen;
1372   size_t msize;
1373   uint32_t s = 0;
1374
1375   GNUNET_assert (NULL != address);
1376   namelen =
1377       (address->transport_name ==
1378        NULL) ? 0 : strlen (address->transport_name) + 1;
1379   msize = sizeof (struct AddressUseMessage) + address->address_length + namelen;
1380   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1381       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1382       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1383   {
1384     GNUNET_break (0);
1385     return;
1386   }
1387
1388   if (session != NULL)
1389   {
1390     s = find_session_id (sh, session, &address->peer);
1391     if ((s == NOT_FOUND) && (GNUNET_NO == in_use))
1392     {
1393       /* trying to set unknown address to NO */
1394       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1395                   "Trying to set unknown address to unused for peer `%s', plugin `%s', session %p\n",
1396                   GNUNET_i2s (&address->peer), address->transport_name, session);
1397       GNUNET_break (0);
1398       return;
1399     }
1400     if ((s == NOT_FOUND) && (GNUNET_YES == in_use))
1401     {
1402       /* trying to set new address to YES */
1403       s = find_empty_session_slot (sh, session, &address->peer);
1404       GNUNET_assert (NOT_FOUND != s);
1405     }
1406   }
1407
1408   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1409   p->size = msize;
1410   p->is_init = GNUNET_NO;
1411   m = (struct AddressUseMessage *) &p[1];
1412   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1413   m->header.size = htons (msize);
1414   m->peer = address->peer;
1415   m->in_use = htons (in_use);
1416   m->address_length = htons (address->address_length);
1417   m->address_local_info = htonl ((uint32_t) address->local_info);
1418   m->plugin_name_length = htons (namelen);
1419
1420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421               "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1422               (GNUNET_YES == in_use) ? "YES" : "NO",
1423               GNUNET_i2s (&address->peer), address->transport_name, session);
1424
1425   m->session_id = htonl (s);
1426   pm = (char *) &m[1];
1427   memcpy (pm, address->address, address->address_length);
1428   memcpy (&pm[address->address_length], address->transport_name, namelen);
1429   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1430   do_transmit (sh);
1431   return;
1432 }
1433
1434
1435 /**
1436  * A session got destroyed, stop including it as a valid address.
1437  *
1438  * @param sh handle
1439  * @param address the address
1440  * @param session session handle that is no longer valid, can be NULL
1441  */
1442 void
1443 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1444                               const struct GNUNET_HELLO_Address *address,
1445                               struct Session *session)
1446 {
1447   struct PendingMessage *p;
1448   struct AddressDestroyedMessage *m;
1449   char *pm;
1450   size_t namelen;
1451   size_t msize;
1452   uint32_t s = 0;
1453
1454   if (address == NULL)
1455   {
1456     GNUNET_break (0);
1457     return;
1458   }
1459
1460   GNUNET_assert (address->transport_name != NULL);
1461   namelen = strlen (address->transport_name) + 1;
1462   GNUNET_assert (namelen > 1);
1463   msize =
1464       sizeof (struct AddressDestroyedMessage) + address->address_length +
1465       namelen;
1466   if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1467       (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1468       (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1469   {
1470     GNUNET_break (0);
1471     return;
1472   }
1473
1474   s = find_session_id (sh, session, &address->peer);
1475   if ((NULL != session) && (NOT_FOUND == s))
1476   {
1477     /* trying to delete unknown address */
1478     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1479                 "Trying to delete unknown address for peer `%s', plugin `%s', session %p\n",
1480                 GNUNET_i2s (&address->peer), address->transport_name, session);
1481     return;
1482   }
1483
1484   p = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
1485   p->size = msize;
1486   p->is_init = GNUNET_NO;
1487   m = (struct AddressDestroyedMessage *) &p[1];
1488   m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1489   m->header.size = htons (msize);
1490   m->reserved = htonl (0);
1491   m->peer = address->peer;
1492   m->address_length = htons (address->address_length);
1493   m->address_local_info = htonl ((uint32_t) address->local_info);
1494   m->plugin_name_length = htons (namelen);
1495
1496   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497               "Deleting address for peer `%s', plugin `%s', session %p\n",
1498               GNUNET_i2s (&address->peer), address->transport_name, session);
1499
1500   m->session_id = htonl (s);
1501   pm = (char *) &m[1];
1502   memcpy (pm, address->address, address->address_length);
1503   memcpy (&pm[address->address_length], address->transport_name, namelen);
1504   GNUNET_CONTAINER_DLL_insert_tail (sh->pending_head, sh->pending_tail, p);
1505   do_transmit (sh);
1506   remove_session (sh, s, &address->peer);
1507 }
1508
1509 /* end of ats_api_scheduling.c */