fix for mantis bug 0002154:
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.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 /**
22  * @file transport/gnunet-service-transport_neighbours.c
23  * @brief neighbour management
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet-service-transport_neighbours.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet-service-transport_validation.h"
31 #include "gnunet-service-transport_clients.h"
32 #include "gnunet-service-transport.h"
33 #include "gnunet_peerinfo_service.h"
34 #include "gnunet-service-transport_blacklist.h"
35 #include "gnunet_constants.h"
36 #include "transport.h"
37
38
39 /**
40  * Size of the neighbour hash map.
41  */
42 #define NEIGHBOUR_TABLE_SIZE 256
43
44 /**
45  * How often must a peer violate bandwidth quotas before we start
46  * to simply drop its messages?
47  */
48 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
49
50 /**
51  * How often do we send KEEPALIVE messages to each of our neighbours and measure
52  * the latency with this neighbour?
53  * (idle timeout is 5 minutes or 300 seconds, so with 30s interval we
54  * send 10 keepalives in each interval, so 10 messages would need to be
55  * lost in a row for a disconnect).
56  */
57 #define KEEPALIVE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
58
59
60 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
61
62 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
63
64 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
65
66 #define  TEST_NEW_CODE GNUNET_NO
67
68 /**
69  * Entry in neighbours.
70  */
71 struct NeighbourMapEntry;
72
73 GNUNET_NETWORK_STRUCT_BEGIN
74
75 /**
76  * Message a peer sends to another to indicate its
77  * preference for communicating via a particular
78  * session (and the desire to establish a real
79  * connection).
80  */
81 struct SessionConnectMessage
82 {
83   /**
84    * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT'
85    */
86   struct GNUNET_MessageHeader header;
87
88   /**
89    * Always zero.
90    */
91   uint32_t reserved GNUNET_PACKED;
92
93   /**
94    * Absolute time at the sender.  Only the most recent connect
95    * message implies which session is preferred by the sender.
96    */
97   struct GNUNET_TIME_AbsoluteNBO timestamp;
98
99 };
100
101
102 struct SessionDisconnectMessage
103 {
104   /**
105    * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT'
106    */
107   struct GNUNET_MessageHeader header;
108
109   /**
110    * Always zero.
111    */
112   uint32_t reserved GNUNET_PACKED;
113
114   /**
115    * Purpose of the signature.  Extends over the timestamp.
116    * Purpose should be GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
117    */
118   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
119
120   /**
121    * Absolute time at the sender.  Only the most recent connect
122    * message implies which session is preferred by the sender.
123    */
124   struct GNUNET_TIME_AbsoluteNBO timestamp;
125
126   /**
127    * Public key of the sender.
128    */
129   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
130
131   /**
132    * Signature of the peer that sends us the disconnect.  Only
133    * valid if the timestamp is AFTER the timestamp from the
134    * corresponding 'CONNECT' message.
135    */
136   struct GNUNET_CRYPTO_RsaSignature signature;
137
138 };
139 GNUNET_NETWORK_STRUCT_END
140
141 /**
142  * For each neighbour we keep a list of messages
143  * that we still want to transmit to the neighbour.
144  */
145 struct MessageQueue
146 {
147
148   /**
149    * This is a doubly linked list.
150    */
151   struct MessageQueue *next;
152
153   /**
154    * This is a doubly linked list.
155    */
156   struct MessageQueue *prev;
157
158   /**
159    * Once this message is actively being transmitted, which
160    * neighbour is it associated with?
161    */
162   struct NeighbourMapEntry *n;
163
164   /**
165    * Function to call once we're done.
166    */
167   GST_NeighbourSendContinuation cont;
168
169   /**
170    * Closure for 'cont'
171    */
172   void *cont_cls;
173
174   /**
175    * The message(s) we want to transmit, GNUNET_MessageHeader(s)
176    * stuck together in memory.  Allocated at the end of this struct.
177    */
178   const char *message_buf;
179
180   /**
181    * Size of the message buf
182    */
183   size_t message_buf_size;
184
185   /**
186    * At what time should we fail?
187    */
188   struct GNUNET_TIME_Absolute timeout;
189
190 };
191
192
193 enum State
194 {
195   /**
196    * fresh peer or completely disconnected
197    */
198   S_NOT_CONNECTED,
199
200   /**
201    * sent CONNECT message to other peer, waiting for CONNECT_ACK
202    */
203   S_CONNECT_SENT,
204
205   /**
206    * received CONNECT message to other peer, sending CONNECT_ACK
207    */
208   S_CONNECT_RECV,
209
210   /**
211    * received ACK or payload
212    */
213   S_CONNECTED,
214
215   /**
216    * connection ended, fast reconnect
217    */
218   S_FAST_RECONNECT,
219
220   /**
221    * Disconnect in progress
222    */
223   S_DISCONNECT
224 };
225
226 enum Address_State
227 {
228   USED,
229   UNUSED,
230   FRESH,
231 };
232
233
234 /**
235  * Entry in neighbours.
236  */
237 struct NeighbourMapEntry
238 {
239
240   /**
241    * Head of list of messages we would like to send to this peer;
242    * must contain at most one message per client.
243    */
244   struct MessageQueue *messages_head;
245
246   /**
247    * Tail of list of messages we would like to send to this peer; must
248    * contain at most one message per client.
249    */
250   struct MessageQueue *messages_tail;
251
252   /**
253    * Are we currently trying to send a message? If so, which one?
254    */
255   struct MessageQueue *is_active;
256
257   /**
258    * Active session for communicating with the peer.
259    */
260   struct Session *session;
261
262   /**
263    * Address we currently use.
264    */
265   struct GNUNET_HELLO_Address *address;
266
267   /**
268    * Identity of this neighbour.
269    */
270   struct GNUNET_PeerIdentity id;
271
272   /**
273    * ID of task scheduled to run when this peer is about to
274    * time out (will free resources associated with the peer).
275    */
276   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
277
278   /**
279    * ID of task scheduled to send keepalives.
280    */
281   GNUNET_SCHEDULER_TaskIdentifier keepalive_task;
282
283   /**
284    * ID of task scheduled to run when we should try transmitting
285    * the head of the message queue.
286    */
287   GNUNET_SCHEDULER_TaskIdentifier transmission_task;
288
289   /**
290    * Tracker for inbound bandwidth.
291    */
292   struct GNUNET_BANDWIDTH_Tracker in_tracker;
293
294   /**
295    * Inbound bandwidth from ATS, activated when connection is up
296    */
297   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
298
299   /**
300    * Inbound bandwidth from ATS, activated when connection is up
301    */
302   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
303
304   /**
305    * Timestamp of the 'SESSION_CONNECT' message we got from the other peer
306    */
307   struct GNUNET_TIME_Absolute connect_ts;
308
309   /**
310    * When did we sent the last keep-alive message?
311    */
312   struct GNUNET_TIME_Absolute keep_alive_sent;
313
314   /**
315    * Latest calculated latency value
316    */
317   struct GNUNET_TIME_Relative latency;
318
319   /**
320    * Timeout for ATS
321    * We asked ATS for a new address for this peer
322    */
323   GNUNET_SCHEDULER_TaskIdentifier ats_suggest;
324
325   /**
326    * Task the resets the peer state after due to an pending
327    * unsuccessful connection setup
328    */
329   GNUNET_SCHEDULER_TaskIdentifier state_reset;
330
331
332   /**
333    * How often has the other peer (recently) violated the inbound
334    * traffic limit?  Incremented by 10 per violation, decremented by 1
335    * per non-violation (for each time interval).
336    */
337   unsigned int quota_violation_count;
338
339
340   /**
341    * The current state of the peer
342    * Element of enum State
343    */
344   int state;
345
346   /**
347    * Did we sent an KEEP_ALIVE message and are we expecting a response?
348    */
349   int expect_latency_response;
350   int address_state;
351 };
352
353
354 /**
355  * All known neighbours and their HELLOs.
356  */
357 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
358
359 /**
360  * Closure for connect_notify_cb, disconnect_notify_cb and address_change_cb
361  */
362 static void *callback_cls;
363
364 /**
365  * Function to call when we connected to a neighbour.
366  */
367 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
368
369 /**
370  * Function to call when we disconnected from a neighbour.
371  */
372 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
373
374 /**
375  * Function to call when we changed an active address of a neighbour.
376  */
377 static GNUNET_TRANSPORT_PeerIterateCallback address_change_cb;
378
379 /**
380  * counter for connected neighbours
381  */
382 static int neighbours_connected;
383
384 /**
385  * Lookup a neighbour entry in the neighbours hash map.
386  *
387  * @param pid identity of the peer to look up
388  * @return the entry, NULL if there is no existing record
389  */
390 static struct NeighbourMapEntry *
391 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
392 {
393   return GNUNET_CONTAINER_multihashmap_get (neighbours, &pid->hashPubKey);
394 }
395
396 /**
397  * Disconnect from the given neighbour, clean up the record.
398  *
399  * @param n neighbour to disconnect from
400  */
401 static void
402 disconnect_neighbour (struct NeighbourMapEntry *n);
403
404 #define change_state(n, state, ...) change (n, state, __LINE__)
405
406 static int
407 is_connecting (struct NeighbourMapEntry *n)
408 {
409   if ((n->state > S_NOT_CONNECTED) && (n->state < S_CONNECTED))
410     return GNUNET_YES;
411   return GNUNET_NO;
412 }
413
414 static int
415 is_connected (struct NeighbourMapEntry *n)
416 {
417   if (n->state == S_CONNECTED)
418     return GNUNET_YES;
419   return GNUNET_NO;
420 }
421
422 static int
423 is_disconnecting (struct NeighbourMapEntry *n)
424 {
425   if (n->state == S_DISCONNECT)
426     return GNUNET_YES;
427   return GNUNET_NO;
428 }
429
430 static const char *
431 print_state (int state)
432 {
433   switch (state)
434   {
435   case S_CONNECTED:
436     return "S_CONNECTED";
437     break;
438   case S_CONNECT_RECV:
439     return "S_CONNECT_RECV";
440     break;
441   case S_CONNECT_SENT:
442     return "S_CONNECT_SENT";
443     break;
444   case S_DISCONNECT:
445     return "S_DISCONNECT";
446     break;
447   case S_NOT_CONNECTED:
448     return "S_NOT_CONNECTED";
449     break;
450   case S_FAST_RECONNECT:
451     return "S_FAST_RECONNECT";
452     break;
453   default:
454     GNUNET_break (0);
455     break;
456   }
457   return NULL;
458 }
459
460 static int
461 change (struct NeighbourMapEntry *n, int state, int line);
462
463 static void
464 ats_suggest_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
465
466
467 static void
468 reset_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
469 {
470   struct NeighbourMapEntry *n = cls;
471
472   if (n == NULL)
473     return;
474
475   n->state_reset = GNUNET_SCHEDULER_NO_TASK;
476   if (n->state == S_CONNECTED)
477     return;
478
479 #if DEBUG_TRANSPORT
480   GNUNET_STATISTICS_update (GST_stats,
481                             gettext_noop
482                             ("# failed connection attempts due to timeout"), 1,
483                             GNUNET_NO);
484 #endif
485
486   /* resetting state */
487
488   if (n->state == S_FAST_RECONNECT)
489   {
490     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491                 "Fast reconnect time out, disconnecting peer `%s'\n",
492                 GNUNET_i2s (&n->id));
493     disconnect_neighbour(n);
494     return;
495   }
496
497   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498               "State for neighbour `%s' %X changed from `%s' to `%s' in line %u\n",
499               GNUNET_i2s (&n->id), n, print_state(n->state), "S_NOT_CONNECTED", __LINE__);
500
501   n->state = S_NOT_CONNECTED;
502
503   /* destroying address */
504   if (n->address != NULL)
505   {
506     GNUNET_assert (strlen (n->address->transport_name) > 0);
507     GNUNET_ATS_address_destroyed (GST_ats, n->address, n->session);
508   }
509
510   /* request new address */
511   if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
512     GNUNET_SCHEDULER_cancel (n->ats_suggest);
513   n->ats_suggest =
514       GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
515                                     n);
516   GNUNET_ATS_suggest_address (GST_ats, &n->id);
517 }
518
519 static int
520 change (struct NeighbourMapEntry *n, int state, int line)
521 {
522   int previous_state;
523   /* allowed transitions */
524   int allowed = GNUNET_NO;
525
526   previous_state = n->state;
527
528   switch (n->state)
529   {
530   case S_NOT_CONNECTED:
531     if ((state == S_CONNECT_RECV) || (state == S_CONNECT_SENT) ||
532         (state == S_DISCONNECT))
533       allowed = GNUNET_YES;
534     break;
535   case S_CONNECT_RECV:
536     allowed = GNUNET_YES;
537     break;
538   case S_CONNECT_SENT:
539     allowed = GNUNET_YES;
540     break;
541   case S_CONNECTED:
542     if ((state == S_DISCONNECT) || (state == S_FAST_RECONNECT))
543       allowed = GNUNET_YES;
544     break;
545   case S_DISCONNECT:
546     break;
547   case S_FAST_RECONNECT:
548     if ((state == S_CONNECTED) || (state == S_DISCONNECT))
549       allowed = GNUNET_YES;
550     break;
551   default:
552     GNUNET_break (0);
553     break;
554   }
555   if (allowed == GNUNET_NO)
556   {
557     char *old = GNUNET_strdup (print_state (n->state));
558     char *new = GNUNET_strdup (print_state (state));
559
560     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
561                 "Illegal state transition from `%s' to `%s' in line %u \n", old,
562                 new, line);
563     GNUNET_break (0);
564     GNUNET_free (old);
565     GNUNET_free (new);
566     return GNUNET_SYSERR;
567   }
568   {
569     char *old = GNUNET_strdup (print_state (n->state));
570     char *new = GNUNET_strdup (print_state (state));
571
572     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573                 "State for neighbour `%s' %X changed from `%s' to `%s' in line %u\n",
574                 GNUNET_i2s (&n->id), n, old, new, line);
575     GNUNET_free (old);
576     GNUNET_free (new);
577   }
578   n->state = state;
579
580   switch (n->state)
581   {
582   case S_FAST_RECONNECT:
583   case S_CONNECT_RECV:
584   case S_CONNECT_SENT:
585     if (n->state_reset != GNUNET_SCHEDULER_NO_TASK)
586       GNUNET_SCHEDULER_cancel (n->state_reset);
587     n->state_reset =
588         GNUNET_SCHEDULER_add_delayed (SETUP_CONNECTION_TIMEOUT, &reset_task, n);
589     break;
590   case S_CONNECTED:
591   case S_NOT_CONNECTED:
592   case S_DISCONNECT:
593     if (GNUNET_SCHEDULER_NO_TASK != n->state_reset)
594     {
595 #if DEBUG_TRANSPORT
596       char *old = GNUNET_strdup (print_state (n->state));
597       char *new = GNUNET_strdup (print_state (state));
598
599       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600                   "Removed reset task for peer `%s' %s failed in state transition `%s' -> `%s' \n",
601                   GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), old, new);
602       GNUNET_free (old);
603       GNUNET_free (new);
604 #endif
605       GNUNET_assert (n->state_reset != GNUNET_SCHEDULER_NO_TASK);
606       GNUNET_SCHEDULER_cancel (n->state_reset);
607       n->state_reset = GNUNET_SCHEDULER_NO_TASK;
608     }
609     break;
610
611   default:
612     GNUNET_assert (0);
613   }
614
615   if (NULL != address_change_cb)
616   {
617     if (n->state == S_CONNECTED)
618       address_change_cb (callback_cls, &n->id, n->address);
619     else if (previous_state == S_CONNECTED)
620       address_change_cb (callback_cls, &n->id, NULL);
621   }
622
623   return GNUNET_OK;
624 }
625
626 static ssize_t
627 send_with_session (struct NeighbourMapEntry *n,
628                    const char *msgbuf, size_t msgbuf_size,
629                    uint32_t priority,
630                    struct GNUNET_TIME_Relative timeout,
631                    GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
632 {
633   struct GNUNET_TRANSPORT_PluginFunctions *papi;
634   size_t ret = GNUNET_SYSERR;
635
636   GNUNET_assert (n != NULL);
637   GNUNET_assert (n->session != NULL);
638
639   papi = GST_plugins_find (n->address->transport_name);
640   if (papi == NULL)
641   {
642     if (cont != NULL)
643       cont (cont_cls, &n->id, GNUNET_SYSERR);
644     return GNUNET_SYSERR;
645   }
646
647   ret = papi->send (papi->cls,
648                    n->session,
649                    msgbuf, msgbuf_size,
650                    0,
651                    timeout,
652                    cont, cont_cls);
653
654   if ((ret == -1) && (cont != NULL))
655       cont (cont_cls, &n->id, GNUNET_SYSERR);
656   return ret;
657 }
658
659 /**
660  * Task invoked to start a transmission to another peer.
661  *
662  * @param cls the 'struct NeighbourMapEntry'
663  * @param tc scheduler context
664  */
665 static void
666 transmission_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
667
668
669 /**
670  * We're done with our transmission attempt, continue processing.
671  *
672  * @param cls the 'struct MessageQueue' of the message
673  * @param receiver intended receiver
674  * @param success whether it worked or not
675  */
676 static void
677 transmit_send_continuation (void *cls,
678                             const struct GNUNET_PeerIdentity *receiver,
679                             int success)
680 {
681   struct MessageQueue *mq = cls;
682   struct NeighbourMapEntry *n;
683   struct NeighbourMapEntry *tmp;
684
685   tmp = lookup_neighbour (receiver);
686   n = mq->n;
687   if ((NULL != n) && (tmp != NULL) && (tmp == n))
688   {
689     GNUNET_assert (n->is_active == mq);
690     n->is_active = NULL;
691     if (success == GNUNET_YES)
692     {
693       GNUNET_assert (n->transmission_task == GNUNET_SCHEDULER_NO_TASK);
694       n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
695     }
696   }
697 #if DEBUG_TRANSPORT
698   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message of type %u was %s\n",
699               ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
700               (success == GNUNET_OK) ? "successful" : "FAILED");
701 #endif
702   if (NULL != mq->cont)
703     mq->cont (mq->cont_cls, success);
704   GNUNET_free (mq);
705 }
706
707
708 /**
709  * Check the ready list for the given neighbour and if a plugin is
710  * ready for transmission (and if we have a message), do so!
711  *
712  * @param n target peer for which to transmit
713  */
714 static void
715 try_transmission_to_peer (struct NeighbourMapEntry *n)
716 {
717   struct MessageQueue *mq;
718   struct GNUNET_TIME_Relative timeout;
719   ssize_t ret;
720
721   if (n->is_active != NULL)
722   {
723     GNUNET_break (0);
724     return;                     /* transmission already pending */
725   }
726   if (n->transmission_task != GNUNET_SCHEDULER_NO_TASK)
727   {
728     GNUNET_break (0);
729     return;                     /* currently waiting for bandwidth */
730   }
731   while (NULL != (mq = n->messages_head))
732   {
733     timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
734     if (timeout.rel_value > 0)
735       break;
736     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
737     n->is_active = mq;
738     mq->n = n;
739     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);     /* timeout */
740   }
741   if (NULL == mq)
742     return;                     /* no more messages */
743
744   if (n->address == NULL)
745   {
746 #if DEBUG_TRANSPORT
747     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No address for peer `%s'\n",
748                 GNUNET_i2s (&n->id));
749 #endif
750     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
751     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);
752     GNUNET_assert (n->transmission_task == GNUNET_SCHEDULER_NO_TASK);
753     n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
754     return;
755   }
756
757   if (GST_plugins_find (n->address->transport_name) == NULL)
758   {
759     GNUNET_break (0);
760     return;
761   }
762   GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
763   n->is_active = mq;
764   mq->n = n;
765
766   if ((n->address->address_length == 0) && (n->session == NULL))
767   {
768     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No address for peer `%s'\n",
769                 GNUNET_i2s (&n->id));
770     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);
771     GNUNET_assert (n->transmission_task == GNUNET_SCHEDULER_NO_TASK);
772     n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
773     return;
774   }
775
776   ret = send_with_session(n,
777               mq->message_buf, mq->message_buf_size,
778               0, timeout,
779               &transmit_send_continuation, mq);
780
781   if (ret == -1)
782   {
783     /* failure, but 'send' would not call continuation in this case,
784      * so we need to do it here! */
785     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);
786   }
787
788 }
789
790
791 /**
792  * Task invoked to start a transmission to another peer.
793  *
794  * @param cls the 'struct NeighbourMapEntry'
795  * @param tc scheduler context
796  */
797 static void
798 transmission_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
799 {
800   struct NeighbourMapEntry *n = cls;
801
802   GNUNET_assert (NULL != lookup_neighbour (&n->id));
803   n->transmission_task = GNUNET_SCHEDULER_NO_TASK;
804   try_transmission_to_peer (n);
805 }
806
807
808 /**
809  * Initialize the neighbours subsystem.
810  *
811  * @param cls closure for callbacks
812  * @param connect_cb function to call if we connect to a peer
813  * @param disconnect_cb function to call if we disconnect from a peer
814  * @param peer_address_cb function to call if we change an active address
815  *                   of a neighbour
816  */
817 void
818 GST_neighbours_start (void *cls,
819                       GNUNET_TRANSPORT_NotifyConnect connect_cb,
820                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
821                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb)
822 {
823   callback_cls = cls;
824   connect_notify_cb = connect_cb;
825   disconnect_notify_cb = disconnect_cb;
826   address_change_cb = peer_address_cb;
827   neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
828 }
829
830
831 static void
832 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
833                       int result)
834 {
835 #if DEBUG_TRANSPORT
836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
837               "Sending DISCONNECT message to peer `%4s': %i\n",
838               GNUNET_i2s (target), result);
839 #endif
840 }
841
842
843 static int
844 send_disconnect (struct NeighbourMapEntry * n)
845 {
846   size_t ret;
847   struct SessionDisconnectMessage disconnect_msg;
848
849 #if DEBUG_TRANSPORT
850   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
851               "Sending DISCONNECT message to peer `%4s'\n",
852               GNUNET_i2s (&n->id));
853 #endif
854
855   disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
856   disconnect_msg.header.type =
857       htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
858   disconnect_msg.reserved = htonl (0);
859   disconnect_msg.purpose.size =
860       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
861              sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
862              sizeof (struct GNUNET_TIME_AbsoluteNBO));
863   disconnect_msg.purpose.purpose =
864       htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
865   disconnect_msg.timestamp =
866       GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
867   disconnect_msg.public_key = GST_my_public_key;
868   GNUNET_assert (GNUNET_OK ==
869                  GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
870                                          &disconnect_msg.purpose,
871                                          &disconnect_msg.signature));
872
873   ret = send_with_session (n,
874             (const char *) &disconnect_msg, sizeof (disconnect_msg),
875             UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
876             &send_disconnect_cont, NULL);
877
878   if (ret == GNUNET_SYSERR)
879     return GNUNET_SYSERR;
880
881   GNUNET_STATISTICS_update (GST_stats,
882                             gettext_noop
883                             ("# peers disconnected due to external request"), 1,
884                             GNUNET_NO);
885   return GNUNET_OK;
886 }
887
888
889 /**
890  * Disconnect from the given neighbour, clean up the record.
891  *
892  * @param n neighbour to disconnect from
893  */
894 static void
895 disconnect_neighbour (struct NeighbourMapEntry *n)
896 {
897   struct MessageQueue *mq;
898   int previous_state;
899
900   previous_state = n->state;
901
902   if (is_disconnecting (n))
903     return;
904
905   /* send DISCONNECT MESSAGE */
906   if (previous_state == S_CONNECTED)
907   {
908     if (GNUNET_OK == send_disconnect (n))
909       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent DISCONNECT_MSG to `%s'\n",
910                   GNUNET_i2s (&n->id));
911     else
912       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
913                   "Could not send DISCONNECT_MSG to `%s'\n",
914                   GNUNET_i2s (&n->id));
915   }
916
917   change_state (n, S_DISCONNECT);
918
919   if (previous_state == S_CONNECTED)
920   {
921     GNUNET_assert (NULL != n->address);
922     if (n->address_state == USED)
923     {
924       GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
925       GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
926       n->address_state = UNUSED;
927     }
928   }
929
930   if (n->address != NULL)
931   {
932     struct GNUNET_TRANSPORT_PluginFunctions *papi;
933
934     papi = GST_plugins_find (n->address->transport_name);
935     if (papi != NULL)
936       papi->disconnect (papi->cls, &n->id);
937   }
938   while (NULL != (mq = n->messages_head))
939   {
940     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
941     if (NULL != mq->cont)
942       mq->cont (mq->cont_cls, GNUNET_SYSERR);
943     GNUNET_free (mq);
944   }
945   if (NULL != n->is_active)
946   {
947     n->is_active->n = NULL;
948     n->is_active = NULL;
949   }
950
951   switch (previous_state)
952   {
953   case S_CONNECTED:
954     GNUNET_assert (neighbours_connected > 0);
955     neighbours_connected--;
956     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != n->keepalive_task);
957     GNUNET_SCHEDULER_cancel (n->keepalive_task);
958     n->keepalive_task = GNUNET_SCHEDULER_NO_TASK;
959     n->expect_latency_response = GNUNET_NO;
960     GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), -1,
961                               GNUNET_NO);
962     disconnect_notify_cb (callback_cls, &n->id);
963     break;
964   case S_FAST_RECONNECT:
965     GNUNET_STATISTICS_update (GST_stats,
966                               gettext_noop ("# fast reconnects failed"), 1,
967                               GNUNET_NO);
968     disconnect_notify_cb (callback_cls, &n->id);
969     break;
970   default:
971     break;
972   }
973
974   GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
975
976   GNUNET_assert (GNUNET_YES ==
977                  GNUNET_CONTAINER_multihashmap_remove (neighbours,
978                                                        &n->id.hashPubKey, n));
979   if (GNUNET_SCHEDULER_NO_TASK != n->ats_suggest)
980   {
981     GNUNET_SCHEDULER_cancel (n->ats_suggest);
982     n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
983   }
984   if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
985   {
986     GNUNET_SCHEDULER_cancel (n->timeout_task);
987     n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
988   }
989   if (GNUNET_SCHEDULER_NO_TASK != n->transmission_task)
990   {
991     GNUNET_SCHEDULER_cancel (n->transmission_task);
992     n->transmission_task = GNUNET_SCHEDULER_NO_TASK;
993   }
994   if (NULL != n->address)
995   {
996     GNUNET_HELLO_address_free (n->address);
997     n->address = NULL;
998   }
999   n->session = NULL;
1000   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting peer `%4s', %X\n",
1001               GNUNET_i2s (&n->id), n);
1002   GNUNET_free (n);
1003 }
1004
1005
1006 /**
1007  * Peer has been idle for too long. Disconnect.
1008  *
1009  * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
1010  * @param tc scheduler context
1011  */
1012 static void
1013 neighbour_timeout_task (void *cls,
1014                         const struct GNUNET_SCHEDULER_TaskContext *tc)
1015 {
1016   struct NeighbourMapEntry *n = cls;
1017
1018   n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1019
1020   GNUNET_STATISTICS_update (GST_stats,
1021                             gettext_noop
1022                             ("# peers disconnected due to timeout"), 1,
1023                             GNUNET_NO);
1024   disconnect_neighbour (n);
1025 }
1026
1027
1028 /**
1029  * Send another keepalive message.
1030  *
1031  * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
1032  * @param tc scheduler context
1033  */
1034 static void
1035 neighbour_keepalive_task (void *cls,
1036                           const struct GNUNET_SCHEDULER_TaskContext *tc)
1037 {
1038   struct NeighbourMapEntry *n = cls;
1039   struct GNUNET_MessageHeader m;
1040   int ret;
1041
1042   GNUNET_assert (S_CONNECTED == n->state);
1043   n->keepalive_task =
1044       GNUNET_SCHEDULER_add_delayed (KEEPALIVE_FREQUENCY,
1045                                     &neighbour_keepalive_task, n);
1046
1047   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1048                             GNUNET_NO);
1049   m.size = htons (sizeof (struct GNUNET_MessageHeader));
1050   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1051
1052   ret = send_with_session (n,
1053             (const void *) &m, sizeof (m),
1054             UINT32_MAX /* priority */ ,
1055             GNUNET_TIME_UNIT_FOREVER_REL,
1056             NULL, NULL);
1057
1058   n->expect_latency_response = GNUNET_NO;
1059   n->keep_alive_sent = GNUNET_TIME_absolute_get_zero ();
1060   if (ret != GNUNET_SYSERR)
1061   {
1062     n->expect_latency_response = GNUNET_YES;
1063     n->keep_alive_sent = GNUNET_TIME_absolute_get ();
1064   }
1065
1066 }
1067
1068
1069 /**
1070  * Disconnect from the given neighbour.
1071  *
1072  * @param cls unused
1073  * @param key hash of neighbour's public key (not used)
1074  * @param value the 'struct NeighbourMapEntry' of the neighbour
1075  */
1076 static int
1077 disconnect_all_neighbours (void *cls, const GNUNET_HashCode * key, void *value)
1078 {
1079   struct NeighbourMapEntry *n = value;
1080
1081 #if DEBUG_TRANSPORT
1082   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
1083               GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
1084 #endif
1085   if (S_CONNECTED == n->state)
1086     GNUNET_STATISTICS_update (GST_stats,
1087                               gettext_noop
1088                               ("# peers disconnected due to global disconnect"),
1089                               1, GNUNET_NO);
1090   disconnect_neighbour (n);
1091   return GNUNET_OK;
1092 }
1093
1094
1095 static void
1096 ats_suggest_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1097 {
1098   struct NeighbourMapEntry *n = cls;
1099
1100   n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
1101
1102   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1103               "ATS did not suggested address to connect to peer `%s'\n",
1104               GNUNET_i2s (&n->id));
1105
1106   disconnect_neighbour (n);
1107 }
1108
1109 /**
1110  * Cleanup the neighbours subsystem.
1111  */
1112 void
1113 GST_neighbours_stop ()
1114 {
1115   // This can happen during shutdown
1116   if (neighbours == NULL)
1117   {
1118     return;
1119   }
1120
1121   GNUNET_CONTAINER_multihashmap_iterate (neighbours, &disconnect_all_neighbours,
1122                                          NULL);
1123   GNUNET_CONTAINER_multihashmap_destroy (neighbours);
1124 //  GNUNET_assert (neighbours_connected == 0);
1125   neighbours = NULL;
1126   callback_cls = NULL;
1127   connect_notify_cb = NULL;
1128   disconnect_notify_cb = NULL;
1129   address_change_cb = NULL;
1130 }
1131
1132 struct ContinutionContext
1133 {
1134   struct GNUNET_HELLO_Address *address;
1135
1136   struct Session *session;
1137 };
1138
1139 static void
1140 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
1141                      struct GNUNET_BANDWIDTH_Value32NBO quota)
1142 {
1143   struct QuotaSetMessage q_msg;
1144
1145 #if DEBUG_TRANSPORT
1146   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147               "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
1148               ntohl (quota.value__), GNUNET_i2s (target));
1149 #endif
1150   q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
1151   q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
1152   q_msg.quota = quota;
1153   q_msg.peer = (*target);
1154   GST_clients_broadcast (&q_msg.header, GNUNET_NO);
1155 }
1156
1157 /**
1158  * We tried to send a SESSION_CONNECT message to another peer.  If this
1159  * succeeded, we change the state.  If it failed, we should tell
1160  * ATS to not use this address anymore (until it is re-validated).
1161  *
1162  * @param cls the 'struct GNUNET_HELLO_Address' of the address that was tried
1163  * @param target peer to send the message to
1164  * @param success GNUNET_OK on success
1165  */
1166 static void
1167 send_connect_continuation (void *cls, const struct GNUNET_PeerIdentity *target,
1168                            int success)
1169 {
1170   struct ContinutionContext *cc = cls;
1171   struct NeighbourMapEntry *n = lookup_neighbour (&cc->address->peer);
1172
1173   if (GNUNET_YES != success)
1174   {
1175     GNUNET_assert (strlen (cc->address->transport_name) > 0);
1176     GNUNET_ATS_address_destroyed (GST_ats, cc->address, cc->session);
1177   }
1178   if ((NULL == neighbours) || (NULL == n) || (n->state == S_DISCONNECT))
1179   {
1180     GNUNET_HELLO_address_free (cc->address);
1181     GNUNET_free (cc);
1182     return;
1183   }
1184
1185   if ((GNUNET_YES == success) &&
1186       ((n->state == S_NOT_CONNECTED) || (n->state == S_CONNECT_SENT)))
1187   {
1188     change_state (n, S_CONNECT_SENT);
1189     GNUNET_HELLO_address_free (cc->address);
1190     GNUNET_free (cc);
1191     return;
1192   }
1193
1194   if ((GNUNET_NO == success) &&
1195       ((n->state == S_NOT_CONNECTED) || (n->state == S_CONNECT_SENT)))
1196   {
1197 #if DEBUG_TRANSPORT
1198     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1199                 "Failed to send CONNECT_MSG to peer `%4s' with address '%s' session %p, asking ATS for new address \n",
1200                 GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
1201 #endif
1202     change_state (n, S_NOT_CONNECTED);
1203     if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1204       GNUNET_SCHEDULER_cancel (n->ats_suggest);
1205     n->ats_suggest =
1206         GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, &ats_suggest_cancel,
1207                                       n);
1208     GNUNET_ATS_suggest_address (GST_ats, &n->id);
1209   }
1210   GNUNET_HELLO_address_free (cc->address);
1211   GNUNET_free (cc);
1212 }
1213
1214
1215 /**
1216  * We tried to switch addresses with an peer already connected. If it failed,
1217  * we should tell ATS to not use this address anymore (until it is re-validated).
1218  *
1219  * @param cls the 'struct NeighbourMapEntry'
1220  * @param target peer to send the message to
1221  * @param success GNUNET_OK on success
1222  */
1223 static void
1224 send_switch_address_continuation (void *cls,
1225                                   const struct GNUNET_PeerIdentity *target,
1226                                   int success)
1227 {
1228   struct ContinutionContext *cc = cls;
1229   struct NeighbourMapEntry *n;
1230
1231   if (neighbours == NULL)
1232   {
1233     GNUNET_HELLO_address_free (cc->address);
1234     GNUNET_free (cc);
1235     return;                     /* neighbour is going away */
1236   }
1237
1238   n = lookup_neighbour (&cc->address->peer);
1239   if ((n == NULL) || (is_disconnecting (n)))
1240   {
1241     GNUNET_HELLO_address_free (cc->address);
1242     GNUNET_free (cc);
1243     return;                     /* neighbour is going away */
1244   }
1245
1246   GNUNET_assert ((n->state == S_CONNECTED) || (n->state == S_FAST_RECONNECT));
1247   if (GNUNET_YES != success)
1248   {
1249 #if DEBUG_TRANSPORT
1250     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251                 "Failed to switch connected peer `%s' to address '%s' session %X, asking ATS for new address \n",
1252                 GNUNET_i2s (&n->id), GST_plugins_a2s (cc->address), cc->session);
1253 #endif
1254     GNUNET_assert (strlen (cc->address->transport_name) > 0);
1255     GNUNET_ATS_address_destroyed (GST_ats, cc->address, cc->session);
1256
1257     if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1258       GNUNET_SCHEDULER_cancel (n->ats_suggest);
1259     n->ats_suggest =
1260         GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
1261                                       n);
1262     GNUNET_ATS_suggest_address (GST_ats, &n->id);
1263     GNUNET_HELLO_address_free (cc->address);
1264     GNUNET_free (cc);
1265     return;
1266   }
1267   /* Tell ATS that switching addresses was successful */
1268   switch (n->state)
1269   {
1270   case S_CONNECTED:
1271     if (n->address_state == FRESH)
1272     {
1273       GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES);
1274       GNUNET_ATS_address_update (GST_ats, cc->address, cc->session, NULL, 0);
1275       if (cc->session != n->session)
1276         GNUNET_break (0);
1277       GNUNET_ATS_address_in_use (GST_ats, cc->address, cc->session, GNUNET_YES);
1278       n->address_state = USED;
1279     }
1280     break;
1281   case S_FAST_RECONNECT:
1282 #if DEBUG_TRANSPORT
1283     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1284                 "Successful fast reconnect to peer `%s'\n",
1285                 GNUNET_i2s (&n->id));
1286 #endif
1287     change_state (n, S_CONNECTED);
1288     neighbours_connected++;
1289     GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), 1,
1290                               GNUNET_NO);
1291
1292     if (n->address_state == FRESH)
1293     {
1294       GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES);
1295       GNUNET_ATS_address_update (GST_ats, cc->address, cc->session, NULL, 0);
1296       GNUNET_ATS_address_in_use (GST_ats, cc->address, cc->session, GNUNET_YES);
1297       n->address_state = USED;
1298     }
1299
1300     if (n->keepalive_task == GNUNET_SCHEDULER_NO_TASK)
1301       n->keepalive_task =
1302           GNUNET_SCHEDULER_add_now (&neighbour_keepalive_task, n);
1303
1304     /* Updating quotas */
1305     GST_neighbours_set_incoming_quota (&n->id, n->bandwidth_in);
1306     send_outbound_quota (target, n->bandwidth_out);
1307
1308   default:
1309     break;
1310   }
1311   GNUNET_HELLO_address_free (cc->address);
1312   GNUNET_free (cc);
1313 }
1314
1315
1316 /**
1317  * We tried to send a SESSION_CONNECT message to another peer.  If this
1318  * succeeded, we change the state.  If it failed, we should tell
1319  * ATS to not use this address anymore (until it is re-validated).
1320  *
1321  * @param cls the 'struct NeighbourMapEntry'
1322  * @param target peer to send the message to
1323  * @param success GNUNET_OK on success
1324  */
1325 static void
1326 send_connect_ack_continuation (void *cls,
1327                                const struct GNUNET_PeerIdentity *target,
1328                                int success)
1329 {
1330   struct ContinutionContext *cc = cls;
1331   struct NeighbourMapEntry *n;
1332
1333   if (neighbours == NULL)
1334   {
1335     GNUNET_HELLO_address_free (cc->address);
1336     GNUNET_free (cc);
1337     return;                     /* neighbour is going away */
1338   }
1339
1340   n = lookup_neighbour (&cc->address->peer);
1341   if ((n == NULL) || (is_disconnecting (n)))
1342   {
1343     GNUNET_HELLO_address_free (cc->address);
1344     GNUNET_free (cc);
1345     return;                     /* neighbour is going away */
1346   }
1347
1348   if (GNUNET_YES == success)
1349   {
1350     GNUNET_HELLO_address_free (cc->address);
1351     GNUNET_free (cc);
1352     return;                     /* sending successful */
1353   }
1354
1355   /* sending failed, ask for next address  */
1356 #if DEBUG_TRANSPORT
1357   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1358               "Failed to send CONNECT_MSG to peer `%4s' with address '%s' session %X, asking ATS for new address \n",
1359               GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
1360 #endif
1361   change_state (n, S_NOT_CONNECTED);
1362   GNUNET_assert (strlen (cc->address->transport_name) > 0);
1363   GNUNET_ATS_address_destroyed (GST_ats, cc->address, cc->session);
1364
1365   if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1366     GNUNET_SCHEDULER_cancel (n->ats_suggest);
1367   n->ats_suggest =
1368       GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
1369                                     n);
1370   GNUNET_ATS_suggest_address (GST_ats, &n->id);
1371   GNUNET_HELLO_address_free (cc->address);
1372   GNUNET_free (cc);
1373 }
1374
1375
1376 /**
1377  * For an existing neighbour record, set the active connection to
1378  * use the given address.
1379  *
1380  * @param peer identity of the peer to switch the address for
1381  * @param address address of the other peer, NULL if other peer
1382  *                       connected to us
1383  * @param session session to use (or NULL)
1384  * @param ats performance data
1385  * @param ats_count number of entries in ats
1386  * @param bandwidth_in inbound quota to be used when connection is up
1387  * @param bandwidth_out outbound quota to be used when connection is up
1388  * @return GNUNET_YES if we are currently connected, GNUNET_NO if the
1389  *         connection is not up (yet)
1390  */
1391 int
1392 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
1393                                        const struct GNUNET_HELLO_Address
1394                                        *address,
1395                                        struct Session *session,
1396                                        const struct GNUNET_ATS_Information *ats,
1397                                        uint32_t ats_count,
1398                                        struct GNUNET_BANDWIDTH_Value32NBO
1399                                        bandwidth_in,
1400                                        struct GNUNET_BANDWIDTH_Value32NBO
1401                                        bandwidth_out)
1402 {
1403   struct NeighbourMapEntry *n;
1404   struct SessionConnectMessage connect_msg;
1405   struct ContinutionContext *cc;
1406   size_t msg_len;
1407   size_t ret;
1408
1409   if (neighbours == NULL)
1410   {
1411     /* This can happen during shutdown */
1412     return GNUNET_NO;
1413   }
1414   n = lookup_neighbour (peer);
1415   if (NULL == n)
1416     return GNUNET_NO;
1417   if (n->state == S_DISCONNECT)
1418   {
1419     /* We are disconnecting, nothing to do here */
1420     return GNUNET_NO;
1421   }
1422   GNUNET_assert (address->transport_name != NULL);
1423   if ((session == NULL) && (0 == address->address_length))
1424   {
1425     GNUNET_break_op (0);
1426     /* FIXME: is this actually possible? When does this happen? */
1427     if (strlen (address->transport_name) > 0)
1428       GNUNET_ATS_address_destroyed (GST_ats, address, session);
1429     GNUNET_ATS_suggest_address (GST_ats, peer);
1430     return GNUNET_NO;
1431   }
1432
1433   /* checks successful and neighbour != NULL */
1434   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435               "ATS tells us to switch to address '%s' session %p for peer `%s' in state `%s'\n",
1436               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
1437               session,
1438               GNUNET_i2s (peer),
1439               print_state (n->state));
1440
1441   if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1442   {
1443     GNUNET_SCHEDULER_cancel (n->ats_suggest);
1444     n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
1445   }
1446   /* do not switch addresses just update quotas */
1447 /*
1448   if (n->state == S_FAST_RECONNECT)
1449   {
1450     if (0 == GNUNET_HELLO_address_cmp(address, n->address))
1451     {
1452       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1453                   "FAST RECONNECT to peer `%s' and  address '%s' with identical ADDRESS\n",
1454                   GNUNET_i2s (&n->id), GST_plugins_a2s (n->address));
1455     }
1456   }
1457 */
1458   if ((n->state == S_CONNECTED) && (NULL != n->address) &&
1459       (0 == GNUNET_HELLO_address_cmp (address, n->address)) &&
1460       (n->session == session))
1461   {
1462     n->bandwidth_in = bandwidth_in;
1463     n->bandwidth_out = bandwidth_out;
1464     GST_neighbours_set_incoming_quota (&n->id, n->bandwidth_in);
1465     send_outbound_quota (peer, n->bandwidth_out);
1466     return GNUNET_NO;
1467   }
1468   if (n->state == S_CONNECTED)
1469   {
1470     /* mark old address as no longer used */
1471     GNUNET_assert (NULL != n->address);
1472     if (n->address_state == USED)
1473     {
1474       GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
1475       GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
1476       n->address_state = UNUSED;
1477     }
1478   }
1479
1480   /* set new address */
1481   if (NULL != n->address)
1482     GNUNET_HELLO_address_free (n->address);
1483   n->address = GNUNET_HELLO_address_copy (address);
1484   n->address_state = FRESH;
1485   n->bandwidth_in = bandwidth_in;
1486   n->bandwidth_out = bandwidth_out;
1487   GNUNET_SCHEDULER_cancel (n->timeout_task);
1488   n->timeout_task =
1489       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
1490                                     &neighbour_timeout_task, n);
1491
1492   if (NULL != address_change_cb && n->state == S_CONNECTED)
1493     address_change_cb (callback_cls, &n->id, n->address); 
1494
1495   /* Obtain an session for this address from plugin */
1496   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1497   papi = GST_plugins_find (address->transport_name);
1498   GNUNET_assert (papi != NULL);
1499   if (session == NULL)
1500   {
1501     n->session = papi->get_session (papi->cls, address);
1502     /* Session could not be initiated */
1503     if (n->session == NULL)
1504     {
1505       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1506                   "Failed to obtain new session %p for peer `%s' and  address '%s'\n",
1507                   n->session, GNUNET_i2s (&n->id), GST_plugins_a2s (n->address));
1508
1509       GNUNET_ATS_address_destroyed (GST_ats, n->address, NULL);
1510
1511       if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1512         GNUNET_SCHEDULER_cancel (n->ats_suggest);
1513       n->ats_suggest =  GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT,
1514                                         ats_suggest_cancel,
1515                                         n);
1516       GNUNET_ATS_suggest_address (GST_ats, &n->id);
1517       GNUNET_HELLO_address_free (n->address);
1518       n->address = NULL;
1519       n->session = NULL;
1520       return GNUNET_NO;
1521     }
1522
1523     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1524                 "Obtained new session %p for peer `%s' and  address '%s'\n",
1525                  n->session, GNUNET_i2s (&n->id), GST_plugins_a2s (n->address));
1526     /* Telling ATS about new session */
1527     GNUNET_ATS_address_update (GST_ats, n->address, n->session, NULL, 0);
1528   }
1529   else
1530   {
1531     n->session = session;
1532     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1533                 "Using existing session %p for peer `%s' and  address '%s'\n",
1534                 n->session,
1535                 GNUNET_i2s (&n->id),
1536                 (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>");
1537   }
1538
1539   switch (n->state)
1540   {
1541   case S_NOT_CONNECTED:
1542   case S_CONNECT_SENT:
1543     msg_len = sizeof (struct SessionConnectMessage);
1544     connect_msg.header.size = htons (msg_len);
1545     connect_msg.header.type =
1546         htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1547     connect_msg.reserved = htonl (0);
1548     connect_msg.timestamp =
1549         GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1550
1551     cc = GNUNET_malloc (sizeof (struct ContinutionContext));
1552     cc->session = n->session;
1553     cc->address = GNUNET_HELLO_address_copy (address);
1554
1555     ret = send_with_session (n,
1556       (const char *) &connect_msg, msg_len,
1557       UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1558       &send_connect_continuation, cc);
1559
1560     return GNUNET_NO;
1561   case S_CONNECT_RECV:
1562     /* We received a CONNECT message and asked ATS for an address */
1563     msg_len = sizeof (struct SessionConnectMessage);
1564     connect_msg.header.size = htons (msg_len);
1565     connect_msg.header.type =
1566         htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1567     connect_msg.reserved = htonl (0);
1568     connect_msg.timestamp =
1569         GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1570     cc = GNUNET_malloc (sizeof (struct ContinutionContext));
1571     cc->session = n->session;
1572     cc->address = GNUNET_HELLO_address_copy (address);
1573
1574     ret = send_with_session(n,
1575                             (const void *) &connect_msg, msg_len,
1576                             UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1577                             &send_connect_ack_continuation,
1578                             cc);
1579     return GNUNET_NO;
1580   case S_CONNECTED:
1581   case S_FAST_RECONNECT:
1582     /* connected peer is switching addresses or tries fast reconnect */
1583     msg_len = sizeof (struct SessionConnectMessage);
1584     connect_msg.header.size = htons (msg_len);
1585     connect_msg.header.type =
1586         htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1587     connect_msg.reserved = htonl (0);
1588     connect_msg.timestamp =
1589         GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1590     cc = GNUNET_malloc (sizeof (struct ContinutionContext));
1591     cc->session = n->session;
1592     cc->address = GNUNET_HELLO_address_copy (address);
1593     ret = send_with_session(n,
1594                             (const void *) &connect_msg, msg_len,
1595                             UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1596                             &send_switch_address_continuation, cc);
1597     if (ret == GNUNET_SYSERR)
1598     {
1599       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1600                   "Failed to send CONNECT_MESSAGE to `%4s' using address '%s' session %X\n",
1601                   GNUNET_i2s (peer), GST_plugins_a2s (address), session);
1602     }
1603     return GNUNET_NO;
1604   default:
1605     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1606                 "Invalid connection state to switch addresses %u \n", n->state);
1607     GNUNET_break_op (0);
1608     return GNUNET_NO;
1609   }
1610 }
1611
1612
1613 /**
1614  * Obtain current latency information for the given neighbour.
1615  *
1616  * @param peer
1617  * @return observed latency of the address, FOREVER if the address was
1618  *         never successfully validated
1619  */
1620 struct GNUNET_TIME_Relative
1621 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
1622 {
1623   struct NeighbourMapEntry *n;
1624
1625   n = lookup_neighbour (peer);
1626   if ((NULL == n) || ((n->address == NULL) && (n->session == NULL)))
1627     return GNUNET_TIME_UNIT_FOREVER_REL;
1628
1629   return n->latency;
1630 }
1631
1632 /**
1633  * Obtain current address information for the given neighbour.
1634  *
1635  * @param peer
1636  * @return address currently used
1637  */
1638 struct GNUNET_HELLO_Address *
1639 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
1640 {
1641   struct NeighbourMapEntry *n;
1642
1643   n = lookup_neighbour (peer);
1644   if ((NULL == n) || ((n->address == NULL) && (n->session == NULL)))
1645     return NULL;
1646
1647   return n->address;
1648 }
1649
1650
1651
1652 /**
1653  * Create an entry in the neighbour map for the given peer
1654  *
1655  * @param peer peer to create an entry for
1656  * @return new neighbour map entry
1657  */
1658 static struct NeighbourMapEntry *
1659 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1660 {
1661   struct NeighbourMapEntry *n;
1662
1663 #if DEBUG_TRANSPORT
1664   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1665               "Unknown peer `%s', creating new neighbour\n", GNUNET_i2s (peer));
1666 #endif
1667   n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
1668   n->id = *peer;
1669   n->state = S_NOT_CONNECTED;
1670   n->latency = GNUNET_TIME_relative_get_forever ();
1671   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1672                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1673                                  MAX_BANDWIDTH_CARRY_S);
1674   n->timeout_task =
1675       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
1676                                     &neighbour_timeout_task, n);
1677   GNUNET_assert (GNUNET_OK ==
1678                  GNUNET_CONTAINER_multihashmap_put (neighbours,
1679                                                     &n->id.hashPubKey, n,
1680                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1681   return n;
1682 }
1683
1684
1685 /**
1686  * Try to create a connection to the given target (eventually).
1687  *
1688  * @param target peer to try to connect to
1689  */
1690 void
1691 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1692 {
1693   struct NeighbourMapEntry *n;
1694
1695   // This can happen during shutdown
1696   if (neighbours == NULL)
1697   {
1698     return;
1699   }
1700 #if DEBUG_TRANSPORT
1701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to peer `%s'\n",
1702               GNUNET_i2s (target));
1703 #endif
1704   if (0 ==
1705       memcmp (target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1706   {
1707     /* my own hello */
1708     return;
1709   }
1710   n = lookup_neighbour (target);
1711
1712   if (NULL != n)
1713   {
1714     if ((S_CONNECTED == n->state) || (is_connecting (n)))
1715       return;                   /* already connecting or connected */
1716     if (is_disconnecting (n))
1717       change_state (n, S_NOT_CONNECTED);
1718   }
1719
1720
1721   if (n == NULL)
1722     n = setup_neighbour (target);
1723 #if DEBUG_TRANSPORT
1724   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1725               "Asking ATS for suggested address to connect to peer `%s'\n",
1726               GNUNET_i2s (&n->id));
1727 #endif
1728
1729   GNUNET_ATS_suggest_address (GST_ats, &n->id);
1730 }
1731
1732 /**
1733  * Test if we're connected to the given peer.
1734  *
1735  * @param target peer to test
1736  * @return GNUNET_YES if we are connected, GNUNET_NO if not
1737  */
1738 int
1739 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
1740 {
1741   struct NeighbourMapEntry *n;
1742
1743   // This can happen during shutdown
1744   if (neighbours == NULL)
1745   {
1746     return GNUNET_NO;
1747   }
1748
1749   n = lookup_neighbour (target);
1750
1751   if ((NULL == n) || (S_CONNECTED != n->state))
1752     return GNUNET_NO;           /* not connected */
1753   return GNUNET_YES;
1754 }
1755
1756 /**
1757  * A session was terminated. Take note.
1758  *
1759  * @param peer identity of the peer where the session died
1760  * @param session session that is gone
1761  */
1762 void
1763 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
1764                                    struct Session *session)
1765 {
1766   struct NeighbourMapEntry *n;
1767
1768   if (neighbours == NULL)
1769   {
1770     /* This can happen during shutdown */
1771     return;
1772   }
1773
1774 #if DEBUG_TRANSPORT
1775   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Session %X to peer `%s' ended \n",
1776               session, GNUNET_i2s (peer));
1777 #endif
1778
1779   n = lookup_neighbour (peer);
1780   if (NULL == n)
1781     return;
1782   if (session != n->session)
1783     return;                     /* doesn't affect us */
1784   if (n->state == S_CONNECTED)
1785   {
1786     if (n->address_state == USED)
1787     {
1788       GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
1789       GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
1790       n->address_state = UNUSED;
1791     }
1792   }
1793
1794   if (NULL != n->address)
1795   {
1796     GNUNET_HELLO_address_free (n->address);
1797     n->address = NULL;
1798   }
1799   n->session = NULL;
1800
1801   /* not connected anymore anyway, shouldn't matter */
1802   if (S_CONNECTED != n->state)
1803     return;
1804
1805   if (n->keepalive_task != GNUNET_SCHEDULER_NO_TASK)
1806   {
1807     GNUNET_SCHEDULER_cancel (n->keepalive_task);
1808     n->keepalive_task = GNUNET_SCHEDULER_NO_TASK;
1809     n->expect_latency_response = GNUNET_NO;
1810   }
1811
1812   /* connected, try fast reconnect */
1813   /* statistics "transport" : "# peers connected" -= 1
1814    * neighbours_connected -= 1
1815    * BUT: no disconnect_cb to notify clients about disconnect
1816    */
1817
1818   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying fast reconnect to peer `%s'\n",
1819               GNUNET_i2s (peer));
1820
1821   GNUNET_assert (neighbours_connected > 0);
1822   change_state (n, S_FAST_RECONNECT);
1823   neighbours_connected--;
1824   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), -1,
1825                             GNUNET_NO);
1826
1827
1828   /* We are connected, so ask ATS to switch addresses */
1829   GNUNET_SCHEDULER_cancel (n->timeout_task);
1830   n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT,
1831                                     &neighbour_timeout_task, n);
1832   /* try QUICKLY to re-establish a connection, reduce timeout! */
1833   if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
1834     GNUNET_SCHEDULER_cancel (n->ats_suggest);
1835   n->ats_suggest = GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT,
1836                                     &ats_suggest_cancel,
1837                                     n);
1838   GNUNET_ATS_suggest_address (GST_ats, peer);
1839 }
1840
1841
1842 /**
1843  * Transmit a message to the given target using the active connection.
1844  *
1845  * @param target destination
1846  * @param msg message to send
1847  * @param msg_size number of bytes in msg
1848  * @param timeout when to fail with timeout
1849  * @param cont function to call when done
1850  * @param cont_cls closure for 'cont'
1851  */
1852 void
1853 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1854                      size_t msg_size, struct GNUNET_TIME_Relative timeout,
1855                      GST_NeighbourSendContinuation cont, void *cont_cls)
1856 {
1857   struct NeighbourMapEntry *n;
1858   struct MessageQueue *mq;
1859
1860   // This can happen during shutdown
1861   if (neighbours == NULL)
1862   {
1863     return;
1864   }
1865
1866   n = lookup_neighbour (target);
1867   if ((n == NULL) || (!is_connected (n)))
1868   {
1869     GNUNET_STATISTICS_update (GST_stats,
1870                               gettext_noop
1871                               ("# messages not sent (no such peer or not connected)"),
1872                               1, GNUNET_NO);
1873 #if DEBUG_TRANSPORT
1874     if (n == NULL)
1875       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1876                   "Could not send message to peer `%s': unknown neighbour",
1877                   GNUNET_i2s (target));
1878     else if (!is_connected (n))
1879       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1880                   "Could not send message to peer `%s': not connected\n",
1881                   GNUNET_i2s (target));
1882 #endif
1883     if (NULL != cont)
1884       cont (cont_cls, GNUNET_SYSERR);
1885     return;
1886   }
1887
1888   if ((n->session == NULL) && (n->address == NULL))
1889   {
1890     GNUNET_STATISTICS_update (GST_stats,
1891                               gettext_noop
1892                               ("# messages not sent (no such peer or not connected)"),
1893                               1, GNUNET_NO);
1894 #if DEBUG_TRANSPORT
1895     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1896                 "Could not send message to peer `%s': no address available\n",
1897                 GNUNET_i2s (target));
1898 #endif
1899
1900     if (NULL != cont)
1901       cont (cont_cls, GNUNET_SYSERR);
1902     return;
1903   }
1904
1905   GNUNET_assert (msg_size >= sizeof (struct GNUNET_MessageHeader));
1906   GNUNET_STATISTICS_update (GST_stats,
1907                             gettext_noop
1908                             ("# bytes in message queue for other peers"),
1909                             msg_size, GNUNET_NO);
1910   mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1911   mq->cont = cont;
1912   mq->cont_cls = cont_cls;
1913   /* FIXME: this memcpy can be up to 7% of our total runtime! */
1914   memcpy (&mq[1], msg, msg_size);
1915   mq->message_buf = (const char *) &mq[1];
1916   mq->message_buf_size = msg_size;
1917   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1918   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1919
1920   if ((GNUNET_SCHEDULER_NO_TASK == n->transmission_task) &&
1921       (NULL == n->is_active))
1922     n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
1923 }
1924
1925
1926 /**
1927  * We have received a message from the given sender.  How long should
1928  * we delay before receiving more?  (Also used to keep the peer marked
1929  * as live).
1930  *
1931  * @param sender sender of the message
1932  * @param size size of the message
1933  * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1934  *                   GNUNET_NO if the neighbour is not connected or violates the quota,
1935  *                   GNUNET_SYSERR if the connection is not fully up yet
1936  * @return how long to wait before reading more from this sender
1937  */
1938 struct GNUNET_TIME_Relative
1939 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1940                                         *sender, ssize_t size, int *do_forward)
1941 {
1942   struct NeighbourMapEntry *n;
1943   struct GNUNET_TIME_Relative ret;
1944
1945   // This can happen during shutdown
1946   if (neighbours == NULL)
1947   {
1948     return GNUNET_TIME_UNIT_FOREVER_REL;
1949   }
1950
1951   n = lookup_neighbour (sender);
1952   if (n == NULL)
1953   {
1954     GST_neighbours_try_connect (sender);
1955     n = lookup_neighbour (sender);
1956     if (NULL == n)
1957     {
1958       GNUNET_STATISTICS_update (GST_stats,
1959                                 gettext_noop
1960                                 ("# messages discarded due to lack of neighbour record"),
1961                                 1, GNUNET_NO);
1962       *do_forward = GNUNET_NO;
1963       return GNUNET_TIME_UNIT_ZERO;
1964     }
1965   }
1966   if (!is_connected (n))
1967   {
1968     *do_forward = GNUNET_SYSERR;
1969     return GNUNET_TIME_UNIT_ZERO;
1970   }
1971   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1972   {
1973     n->quota_violation_count++;
1974 #if DEBUG_TRANSPORT
1975     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1976                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1977                 n->in_tracker.available_bytes_per_s__,
1978                 n->quota_violation_count);
1979 #endif
1980     /* Discount 32k per violation */
1981     GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1982   }
1983   else
1984   {
1985     if (n->quota_violation_count > 0)
1986     {
1987       /* try to add 32k back */
1988       GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1989       n->quota_violation_count--;
1990     }
1991   }
1992   if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1993   {
1994     GNUNET_STATISTICS_update (GST_stats,
1995                               gettext_noop
1996                               ("# bandwidth quota violations by other peers"),
1997                               1, GNUNET_NO);
1998     *do_forward = GNUNET_NO;
1999     return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
2000   }
2001   *do_forward = GNUNET_YES;
2002   ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
2003   if (ret.rel_value > 0)
2004   {
2005 #if DEBUG_TRANSPORT
2006     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2007                 "Throttling read (%llu bytes excess at %u b/s), waiting %llu ms before reading more.\n",
2008                 (unsigned long long) n->in_tracker.
2009                 consumption_since_last_update__,
2010                 (unsigned int) n->in_tracker.available_bytes_per_s__,
2011                 (unsigned long long) ret.rel_value);
2012 #endif
2013     GNUNET_STATISTICS_update (GST_stats,
2014                               gettext_noop ("# ms throttling suggested"),
2015                               (int64_t) ret.rel_value, GNUNET_NO);
2016   }
2017   return ret;
2018 }
2019
2020
2021 /**
2022  * Keep the connection to the given neighbour alive longer,
2023  * we received a KEEPALIVE (or equivalent).
2024  *
2025  * @param neighbour neighbour to keep alive
2026  */
2027 void
2028 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour)
2029 {
2030   struct NeighbourMapEntry *n;
2031
2032   // This can happen during shutdown
2033   if (neighbours == NULL)
2034   {
2035     return;
2036   }
2037
2038   n = lookup_neighbour (neighbour);
2039   if (NULL == n)
2040   {
2041     GNUNET_STATISTICS_update (GST_stats,
2042                               gettext_noop
2043                               ("# KEEPALIVE messages discarded (not connected)"),
2044                               1, GNUNET_NO);
2045     return;
2046   }
2047   GNUNET_SCHEDULER_cancel (n->timeout_task);
2048   n->timeout_task =
2049       GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
2050                                     &neighbour_timeout_task, n);
2051
2052   /* send reply to measure latency */
2053   if (S_CONNECTED != n->state)
2054     return;
2055
2056   struct GNUNET_MessageHeader m;
2057
2058   m.size = htons (sizeof (struct GNUNET_MessageHeader));
2059   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
2060
2061   send_with_session(n,
2062       (const void *) &m, sizeof (m),
2063       UINT32_MAX,
2064       GNUNET_TIME_UNIT_FOREVER_REL,
2065       NULL, NULL);
2066 }
2067
2068 /**
2069  * We received a KEEP_ALIVE_RESPONSE message and use this to calculate latency
2070  * to this peer
2071  *
2072  * @param neighbour neighbour to keep alive
2073  * @param ats performance data
2074  * @param ats_count number of entries in ats
2075  */
2076 void
2077 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
2078                                    const struct GNUNET_ATS_Information *ats,
2079                                    uint32_t ats_count)
2080 {
2081   struct NeighbourMapEntry *n;
2082   struct GNUNET_ATS_Information *ats_new;
2083   uint32_t latency;
2084
2085   if (neighbours == NULL)
2086   {
2087     // This can happen during shutdown
2088     return;
2089   }
2090
2091   n = lookup_neighbour (neighbour);
2092   if ((NULL == n) || (n->state != S_CONNECTED))
2093   {
2094     GNUNET_STATISTICS_update (GST_stats,
2095                               gettext_noop
2096                               ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
2097                               1, GNUNET_NO);
2098     return;
2099   }
2100   if (n->expect_latency_response != GNUNET_YES)
2101   {
2102     GNUNET_STATISTICS_update (GST_stats,
2103                               gettext_noop
2104                               ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
2105                               1, GNUNET_NO);
2106     return;
2107   }
2108   n->expect_latency_response = GNUNET_NO;
2109
2110   GNUNET_assert (n->keep_alive_sent.abs_value !=
2111                  GNUNET_TIME_absolute_get_zero ().abs_value);
2112   n->latency =
2113       GNUNET_TIME_absolute_get_difference (n->keep_alive_sent,
2114                                            GNUNET_TIME_absolute_get ());
2115 #if DEBUG_TRANSPORT
2116   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Latency for peer `%s' is %llu ms\n",
2117               GNUNET_i2s (&n->id), n->latency.rel_value);
2118 #endif
2119
2120
2121   if (n->latency.rel_value == GNUNET_TIME_relative_get_forever ().rel_value)
2122   {
2123     GNUNET_ATS_address_update (GST_ats, n->address, n->session, ats, ats_count);
2124   }
2125   else
2126   {
2127     ats_new =
2128         GNUNET_malloc (sizeof (struct GNUNET_ATS_Information) *
2129                        (ats_count + 1));
2130     memcpy (ats_new, ats, sizeof (struct GNUNET_ATS_Information) * ats_count);
2131
2132     /* add latency */
2133     ats_new[ats_count].type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
2134     if (n->latency.rel_value > UINT32_MAX)
2135       latency = UINT32_MAX;
2136     else
2137       latency = n->latency.rel_value;
2138     ats_new[ats_count].value = htonl (latency);
2139
2140     GNUNET_ATS_address_update (GST_ats, n->address, n->session, ats_new,
2141                                ats_count + 1);
2142     GNUNET_free (ats_new);
2143   }
2144 }
2145
2146
2147 /**
2148  * Change the incoming quota for the given peer.
2149  *
2150  * @param neighbour identity of peer to change qutoa for
2151  * @param quota new quota
2152  */
2153 void
2154 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
2155                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
2156 {
2157   struct NeighbourMapEntry *n;
2158
2159   // This can happen during shutdown
2160   if (neighbours == NULL)
2161   {
2162     return;
2163   }
2164
2165   n = lookup_neighbour (neighbour);
2166   if (n == NULL)
2167   {
2168     GNUNET_STATISTICS_update (GST_stats,
2169                               gettext_noop
2170                               ("# SET QUOTA messages ignored (no such peer)"),
2171                               1, GNUNET_NO);
2172     return;
2173   }
2174 #if DEBUG_TRANSPORT
2175   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2176               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
2177               ntohl (quota.value__), GNUNET_i2s (&n->id));
2178 #endif
2179   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
2180   if (0 != ntohl (quota.value__))
2181     return;
2182 #if DEBUG_TRANSPORT
2183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
2184               GNUNET_i2s (&n->id), "SET_QUOTA");
2185 #endif
2186   if (is_connected (n))
2187     GNUNET_STATISTICS_update (GST_stats,
2188                               gettext_noop ("# disconnects due to quota of 0"),
2189                               1, GNUNET_NO);
2190   disconnect_neighbour (n);
2191 }
2192
2193
2194 /**
2195  * Closure for the neighbours_iterate function.
2196  */
2197 struct IteratorContext
2198 {
2199   /**
2200    * Function to call on each connected neighbour.
2201    */
2202   GST_NeighbourIterator cb;
2203
2204   /**
2205    * Closure for 'cb'.
2206    */
2207   void *cb_cls;
2208 };
2209
2210
2211 /**
2212  * Call the callback from the closure for each connected neighbour.
2213  *
2214  * @param cls the 'struct IteratorContext'
2215  * @param key the hash of the public key of the neighbour
2216  * @param value the 'struct NeighbourMapEntry'
2217  * @return GNUNET_OK (continue to iterate)
2218  */
2219 static int
2220 neighbours_iterate (void *cls, const GNUNET_HashCode * key, void *value)
2221 {
2222   struct IteratorContext *ic = cls;
2223   struct NeighbourMapEntry *n = value;
2224
2225   if (!is_connected (n))
2226     return GNUNET_OK;
2227
2228   ic->cb (ic->cb_cls, &n->id, NULL, 0, n->address);
2229   return GNUNET_OK;
2230 }
2231
2232
2233 /**
2234  * Iterate over all connected neighbours.
2235  *
2236  * @param cb function to call
2237  * @param cb_cls closure for cb
2238  */
2239 void
2240 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
2241 {
2242   struct IteratorContext ic;
2243
2244   // This can happen during shutdown
2245   if (neighbours == NULL)
2246   {
2247     return;
2248   }
2249
2250   ic.cb = cb;
2251   ic.cb_cls = cb_cls;
2252   GNUNET_CONTAINER_multihashmap_iterate (neighbours, &neighbours_iterate, &ic);
2253 }
2254
2255 /**
2256  * If we have an active connection to the given target, it must be shutdown.
2257  *
2258  * @param target peer to disconnect from
2259  */
2260 void
2261 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
2262 {
2263   struct NeighbourMapEntry *n;
2264
2265   // This can happen during shutdown
2266   if (neighbours == NULL)
2267   {
2268     return;
2269   }
2270
2271   n = lookup_neighbour (target);
2272   if (NULL == n)
2273     return;                     /* not active */
2274   disconnect_neighbour (n);
2275 }
2276
2277
2278 /**
2279  * We received a disconnect message from the given peer,
2280  * validate and process.
2281  *
2282  * @param peer sender of the message
2283  * @param msg the disconnect message
2284  */
2285 void
2286 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
2287                                           *peer,
2288                                           const struct GNUNET_MessageHeader
2289                                           *msg)
2290 {
2291   struct NeighbourMapEntry *n;
2292   const struct SessionDisconnectMessage *sdm;
2293   GNUNET_HashCode hc;
2294
2295 #if DEBUG_TRANSPORT
2296   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2297               "Received DISCONNECT message from peer `%s'\n",
2298               GNUNET_i2s (peer));
2299 #endif
2300
2301   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
2302   {
2303     // GNUNET_break_op (0);
2304     GNUNET_STATISTICS_update (GST_stats,
2305                               gettext_noop
2306                               ("# disconnect messages ignored (old format)"), 1,
2307                               GNUNET_NO);
2308     return;
2309   }
2310   sdm = (const struct SessionDisconnectMessage *) msg;
2311   n = lookup_neighbour (peer);
2312   if (NULL == n)
2313     return;                     /* gone already */
2314   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value <=
2315       n->connect_ts.abs_value)
2316   {
2317     GNUNET_STATISTICS_update (GST_stats,
2318                               gettext_noop
2319                               ("# disconnect messages ignored (timestamp)"), 1,
2320                               GNUNET_NO);
2321     return;
2322   }
2323   GNUNET_CRYPTO_hash (&sdm->public_key,
2324                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2325                       &hc);
2326   if (0 != memcmp (peer, &hc, sizeof (struct GNUNET_PeerIdentity)))
2327   {
2328     GNUNET_break_op (0);
2329     return;
2330   }
2331   if (ntohl (sdm->purpose.size) !=
2332       sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
2333       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
2334       sizeof (struct GNUNET_TIME_AbsoluteNBO))
2335   {
2336     GNUNET_break_op (0);
2337     return;
2338   }
2339   if (GNUNET_OK !=
2340       GNUNET_CRYPTO_rsa_verify
2341       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
2342        &sdm->signature, &sdm->public_key))
2343   {
2344     GNUNET_break_op (0);
2345     return;
2346   }
2347   GST_neighbours_force_disconnect (peer);
2348 }
2349
2350
2351 /**
2352  * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2353  * Consider switching to it.
2354  *
2355  * @param message possibly a 'struct SessionConnectMessage' (check format)
2356  * @param peer identity of the peer to switch the address for
2357  * @param address address of the other peer, NULL if other peer
2358  *                       connected to us
2359  * @param session session to use (or NULL)
2360  * @param ats performance data
2361  * @param ats_count number of entries in ats
2362  */
2363 void
2364 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2365                                    const struct GNUNET_PeerIdentity *peer,
2366                                    const struct GNUNET_HELLO_Address *address,
2367                                    struct Session *session,
2368                                    const struct GNUNET_ATS_Information *ats,
2369                                    uint32_t ats_count)
2370 {
2371   const struct SessionConnectMessage *scm;
2372   struct GNUNET_MessageHeader msg;
2373   struct NeighbourMapEntry *n;
2374   size_t msg_len;
2375   size_t ret;
2376
2377   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2378               "Received CONNECT_ACK message from peer `%s'\n",
2379               GNUNET_i2s (peer));
2380
2381   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2382   {
2383     GNUNET_break_op (0);
2384     return;
2385   }
2386   scm = (const struct SessionConnectMessage *) message;
2387   GNUNET_break_op (ntohl (scm->reserved) == 0);
2388   n = lookup_neighbour (peer);
2389   if (NULL == n)
2390   {
2391     /* we did not send 'CONNECT' -- at least not recently */
2392     GNUNET_STATISTICS_update (GST_stats,
2393                               gettext_noop
2394                               ("# unexpected CONNECT_ACK messages (no peer)"),
2395                               1, GNUNET_NO);
2396     return;
2397   }
2398
2399   /* Additional check
2400    *
2401    * ((n->state != S_CONNECT_RECV) && (n->address != NULL)):
2402    *
2403    * We also received an CONNECT message, switched from SENDT to RECV and
2404    * ATS already suggested us an address after a successful blacklist check
2405    */
2406
2407   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2408               "Received CONNECT_ACK message from peer `%s' in state `%s'\n",
2409               GNUNET_i2s (peer),
2410               print_state(n->state));
2411
2412   if ((n->address != NULL) && (n->state == S_CONNECTED))
2413   {
2414     /* After fast reconnect: send ACK (ACK) even when we are connected */
2415     msg_len = sizeof (msg);
2416     msg.size = htons (msg_len);
2417     msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2418
2419     ret = send_with_session(n,
2420               (const char *) &msg, msg_len,
2421               UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2422               NULL, NULL);
2423
2424     if (ret == GNUNET_SYSERR)
2425       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2426                   "Failed to send SESSION_ACK to `%4s' using address '%s' session %X\n",
2427                   GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
2428     return;
2429   }
2430
2431   if ((n->state != S_CONNECT_SENT) &&
2432       ((n->state != S_CONNECT_RECV) && (n->address != NULL)))
2433   {
2434     GNUNET_STATISTICS_update (GST_stats,
2435                               gettext_noop
2436                               ("# unexpected CONNECT_ACK messages"), 1,
2437                               GNUNET_NO);
2438     return;
2439   }
2440   if (n->state != S_CONNECTED)
2441     change_state (n, S_CONNECTED);
2442
2443   if (NULL != session)
2444   {
2445     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2446                      "transport-ats",
2447                      "Giving ATS session %p of plugin %s for peer %s\n",
2448                      session, address->transport_name, GNUNET_i2s (peer));
2449   }
2450   GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
2451   GNUNET_assert (NULL != n->address);
2452
2453   if ((n->address_state == FRESH) && (0 == GNUNET_HELLO_address_cmp(address, n->address)))
2454   {
2455     GST_validation_set_address_use (n->address, n->session, GNUNET_YES);
2456     GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_YES);
2457     n->address_state = USED;
2458   }
2459
2460   GST_neighbours_set_incoming_quota (&n->id, n->bandwidth_in);
2461
2462   /* send ACK (ACK) */
2463   msg_len = sizeof (msg);
2464   msg.size = htons (msg_len);
2465   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2466
2467   ret = send_with_session(n,
2468             (const char *) &msg, msg_len,
2469             UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2470             NULL, NULL);
2471
2472   if (ret == GNUNET_SYSERR)
2473     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2474                 "Failed to send SESSION_ACK to `%4s' using address '%s' session %X\n",
2475                 GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
2476
2477
2478   if (n->keepalive_task == GNUNET_SCHEDULER_NO_TASK)
2479     n->keepalive_task = GNUNET_SCHEDULER_add_now (&neighbour_keepalive_task, n);
2480
2481   neighbours_connected++;
2482   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), 1,
2483                             GNUNET_NO);
2484 #if DEBUG_TRANSPORT
2485   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2486               "Notify about connect of `%4s' using address '%s' session %X LINE %u\n",
2487               GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session,
2488               __LINE__);
2489 #endif
2490   connect_notify_cb (callback_cls, &n->id, ats, ats_count);
2491   send_outbound_quota (peer, n->bandwidth_out);
2492
2493 }
2494
2495
2496 void
2497 GST_neighbours_handle_ack (const struct GNUNET_MessageHeader *message,
2498                            const struct GNUNET_PeerIdentity *peer,
2499                            const struct GNUNET_HELLO_Address *address,
2500                            struct Session *session,
2501                            const struct GNUNET_ATS_Information *ats,
2502                            uint32_t ats_count)
2503 {
2504   struct NeighbourMapEntry *n;
2505
2506 #if DEBUG_TRANSPORT
2507   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ACK message from peer `%s'\n",
2508               GNUNET_i2s (peer));
2509 #endif
2510
2511   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
2512   {
2513     GNUNET_break_op (0);
2514     return;
2515   }
2516   n = lookup_neighbour (peer);
2517   if (NULL == n)
2518   {
2519     GNUNET_break (0);
2520     return;
2521   }
2522   if (S_CONNECTED == n->state)
2523     return;
2524   if (!is_connecting (n))
2525   {
2526     GNUNET_STATISTICS_update (GST_stats,
2527                               gettext_noop ("# unexpected ACK messages"), 1,
2528                               GNUNET_NO);
2529     return;
2530   }
2531   change_state (n, S_CONNECTED);
2532   if (NULL != session)
2533     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2534                      "transport-ats",
2535                      "Giving ATS session %p of plugin %s for peer %s\n",
2536                      session, address->transport_name, GNUNET_i2s (peer));
2537   GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
2538   GNUNET_assert (n->address != NULL);
2539
2540   if ((n->address_state == FRESH) && (0 == GNUNET_HELLO_address_cmp(address, n->address)))
2541   {
2542     GST_validation_set_address_use (n->address, n->session, GNUNET_YES);
2543     GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_YES);
2544     n->address_state = USED;
2545   }
2546
2547
2548   neighbours_connected++;
2549   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), 1,
2550                             GNUNET_NO);
2551
2552   GST_neighbours_set_incoming_quota (&n->id, n->bandwidth_in);
2553   if (n->keepalive_task == GNUNET_SCHEDULER_NO_TASK)
2554     n->keepalive_task = GNUNET_SCHEDULER_add_now (&neighbour_keepalive_task, n);
2555 #if DEBUG_TRANSPORT
2556   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2557               "Notify about connect of `%4s' using address '%s' session %X LINE %u\n",
2558               GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session,
2559               __LINE__);
2560 #endif
2561   connect_notify_cb (callback_cls, &n->id, ats, ats_count);
2562   send_outbound_quota (peer, n->bandwidth_out);
2563 }
2564
2565 struct BlackListCheckContext
2566 {
2567   struct GNUNET_ATS_Information *ats;
2568
2569   uint32_t ats_count;
2570
2571   struct Session *session;
2572
2573   struct GNUNET_HELLO_Address *address;
2574
2575   struct GNUNET_TIME_Absolute ts;
2576 };
2577
2578
2579 static void
2580 handle_connect_blacklist_cont (void *cls,
2581                                const struct GNUNET_PeerIdentity *peer,
2582                                int result)
2583 {
2584   struct NeighbourMapEntry *n;
2585   struct BlackListCheckContext *bcc = cls;
2586
2587 #if DEBUG_TRANSPORT
2588   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2589               "Blacklist check due to CONNECT message: `%s'\n",
2590               GNUNET_i2s (peer),
2591               (result == GNUNET_OK) ? "ALLOWED" : "FORBIDDEN");
2592 #endif
2593
2594   /* not allowed */
2595   if (GNUNET_OK != result)
2596   {
2597     GNUNET_HELLO_address_free (bcc->address);
2598     GNUNET_free (bcc);
2599     return;
2600   }
2601
2602   n = lookup_neighbour (peer);
2603   if (NULL == n)
2604     n = setup_neighbour (peer);
2605
2606   if (bcc->ts.abs_value > n->connect_ts.abs_value)
2607   {
2608     if (NULL != bcc->session)
2609       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
2610                        "transport-ats",
2611                        "Giving ATS session %p of address `%s' for peer %s\n",
2612                        bcc->session, GST_plugins_a2s (bcc->address),
2613                        GNUNET_i2s (peer));
2614     /* Tell ATS about the session, so ATS can suggest it if it likes it. */
2615
2616     GNUNET_ATS_address_update (GST_ats, bcc->address, bcc->session, bcc->ats,
2617                                bcc->ats_count);
2618     n->connect_ts = bcc->ts;
2619   }
2620
2621   GNUNET_HELLO_address_free (bcc->address);
2622   GNUNET_free (bcc);
2623
2624   if (n->state != S_CONNECT_RECV)
2625     change_state (n, S_CONNECT_RECV);
2626
2627
2628   /* Ask ATS for an address to connect via that address */
2629   if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
2630     GNUNET_SCHEDULER_cancel (n->ats_suggest);
2631   n->ats_suggest =
2632       GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
2633                                     n);
2634   GNUNET_ATS_suggest_address (GST_ats, peer);
2635 }
2636
2637 /**
2638  * We received a 'SESSION_CONNECT' message from the other peer.
2639  * Consider switching to it.
2640  *
2641  * @param message possibly a 'struct SessionConnectMessage' (check format)
2642  * @param peer identity of the peer to switch the address for
2643  * @param address address of the other peer, NULL if other peer
2644  *                       connected to us
2645  * @param session session to use (or NULL)
2646  * @param ats performance data
2647  * @param ats_count number of entries in ats (excluding 0-termination)
2648  */
2649 void
2650 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2651                                const struct GNUNET_PeerIdentity *peer,
2652                                const struct GNUNET_HELLO_Address *address,
2653                                struct Session *session,
2654                                const struct GNUNET_ATS_Information *ats,
2655                                uint32_t ats_count)
2656 {
2657   const struct SessionConnectMessage *scm;
2658   struct BlackListCheckContext *bcc = NULL;
2659   struct NeighbourMapEntry *n;
2660
2661 #if DEBUG_TRANSPORT
2662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2663               "Received CONNECT message from peer `%s'\n", GNUNET_i2s (peer));
2664 #endif
2665
2666   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2667   {
2668     GNUNET_break_op (0);
2669     return;
2670   }
2671
2672   scm = (const struct SessionConnectMessage *) message;
2673   GNUNET_break_op (ntohl (scm->reserved) == 0);
2674
2675   GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
2676
2677   n = lookup_neighbour (peer);
2678   if ((n != NULL) && ((S_CONNECTED == n->state) || (S_FAST_RECONNECT == n->state)))
2679   {
2680     /* connected peer switches addresses or is trying to do a fast reconnect*/
2681     return;
2682   }
2683
2684
2685   /* we are not connected to this peer */
2686   /* do blacklist check */
2687   bcc =
2688       GNUNET_malloc (sizeof (struct BlackListCheckContext) +
2689                      sizeof (struct GNUNET_ATS_Information) * (ats_count + 1));
2690   bcc->ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2691   bcc->ats_count = ats_count + 1;
2692   bcc->address = GNUNET_HELLO_address_copy (address);
2693   bcc->session = session;
2694   bcc->ats = (struct GNUNET_ATS_Information *) &bcc[1];
2695   memcpy (bcc->ats, ats, sizeof (struct GNUNET_ATS_Information) * ats_count);
2696   bcc->ats[ats_count].type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
2697   bcc->ats[ats_count].value =
2698       htonl ((uint32_t) GST_neighbour_get_latency (peer).rel_value);
2699   GST_blacklist_test_allowed (peer, address->transport_name,
2700                               &handle_connect_blacklist_cont, bcc);
2701 }
2702
2703
2704 /* end of file gnunet-service-transport_neighbours.c */