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