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