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