error msg
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011,2012 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  * TODO:
27  * - "address_change_cb" is NEVER invoked; when should we call this one exactly?
28  * - TEST, TEST, TEST...
29  */
30 #include "platform.h"
31 #include "gnunet_ats_service.h"
32 #include "gnunet-service-transport_neighbours.h"
33 #include "gnunet-service-transport_plugins.h"
34 #include "gnunet-service-transport_validation.h"
35 #include "gnunet-service-transport_clients.h"
36 #include "gnunet-service-transport.h"
37 #include "gnunet_peerinfo_service.h"
38 #include "gnunet-service-transport_blacklist.h"
39 #include "gnunet_constants.h"
40 #include "transport.h"
41
42
43
44 /**
45  * Size of the neighbour hash map.
46  */
47 #define NEIGHBOUR_TABLE_SIZE 256
48
49 /**
50  * Time we give plugin to transmit DISCONNECT message before the
51  * neighbour entry self-destructs.
52  */
53 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
54
55 /**
56  * How often must a peer violate bandwidth quotas before we start
57  * to simply drop its messages?
58  */
59 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
60
61 /**
62  * How often do we send KEEPALIVE messages to each of our neighbours and measure
63  * the latency with this neighbour?
64  * (idle timeout is 5 minutes or 300 seconds, so with 100s interval we
65  * send 3 keepalives in each interval, so 3 messages would need to be
66  * lost in a row for a disconnect).
67  */
68 #define KEEPALIVE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100)
69
70 /**
71  * How long are we willing to wait for a response from ATS before timing out?
72  */
73 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
74
75 /**
76  * How long are we willing to wait for an ACK from the other peer before
77  * giving up on our connect operation?
78  */
79 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
80
81 /**
82  * How long are we willing to wait for a successful reconnect if 
83  * an existing connection went down?  Much shorter than the
84  * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
85  * higher layers about the disconnect during this period.
86  */
87 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
88
89 /**
90  * How long are we willing to wait for a response from the blacklist
91  * subsystem before timing out?
92  */
93 #define BLACKLIST_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
94
95 #define DEBUG_MALLOC GNUNET_NO
96
97 #if DEBUG_MALLOC
98
99 struct Allocator
100 {
101   struct Allocator *prev;
102   struct Allocator *next;
103
104   unsigned int bytes_alloced;
105   unsigned int max_alloced;
106   unsigned int diff;
107   unsigned int line;
108
109   struct GNUNET_TIME_Absolute max_alloced_when;
110   struct GNUNET_TIME_Absolute last_alloced_when;
111
112 };
113
114 struct Allocator *aehead;
115 struct Allocator *aetail;
116
117 struct Allocation
118 {
119   struct Allocation *prev;
120   struct Allocation *next;
121
122   struct Allocator *alloc;
123   unsigned int bytes_alloced;
124   void *p;
125   unsigned int line;
126 };
127
128 struct Allocation *ahead;
129 struct Allocation *atail;
130
131 static int bytes_alloced;
132
133 static struct Allocator *
134 find_allocator (int line)
135 {
136   struct Allocator *cur = aehead;
137   while (NULL != cur)
138   {
139       if (line == cur->line)
140         return cur;
141       cur = cur->next;
142   }
143   return cur;
144 }
145
146 static void
147 print_allocators ()
148 {
149   static int start = GNUNET_YES;
150   static struct GNUNET_TIME_Absolute next;
151   static struct GNUNET_TIME_Relative rem;
152   struct Allocator *cur = aehead;
153   if (start)
154   {
155       next = GNUNET_TIME_UNIT_ZERO_ABS;
156       start = GNUNET_NO;
157   }
158   if (0 == (rem = GNUNET_TIME_absolute_get_remaining(next)).rel_value)
159   {
160       fprintf (stderr, "Allocated in `%s' total: %5u bytes\n", __FILE__, bytes_alloced);
161       while (NULL != cur)
162       {
163           char *last_alloc = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string(cur->max_alloced_when));
164           fprintf (stderr, "Allocated from line %4u :%5u bytes (diff %5i bytes, max alloc: %5u @ %s, last alloc %s)\n",
165               cur->line, cur->bytes_alloced, cur->diff, cur->max_alloced,
166               last_alloc,
167               GNUNET_STRINGS_absolute_time_to_string(cur->last_alloced_when));
168           GNUNET_free (last_alloc);
169           cur->diff = 0;
170           cur = cur->next;
171       }
172       fprintf (stderr, "\n");
173     next = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), GNUNET_TIME_UNIT_SECONDS);
174   }
175 }
176
177 #endif
178
179 static void
180 MEMDEBUG_add_alloc (void *p, size_t size, int line)
181 {
182 #if DEBUG_MALLOC
183   struct Allocation *alloc = GNUNET_malloc (sizeof (struct Allocation));
184   struct Allocator *allocator = find_allocator(line);
185   if (NULL == allocator)
186   {
187       allocator = GNUNET_malloc (sizeof (struct Allocator));
188       allocator->line = line;
189       GNUNET_CONTAINER_DLL_insert (aehead, aetail, allocator);
190   }
191   alloc->alloc = allocator;
192   alloc->p = p;
193   alloc->line = line;
194   alloc->bytes_alloced = size;
195   allocator->bytes_alloced += size;
196   allocator->last_alloced_when = GNUNET_TIME_absolute_get();
197   if (allocator->bytes_alloced >= allocator->max_alloced)
198   {
199       allocator->max_alloced = allocator->bytes_alloced;
200       allocator->max_alloced_when = allocator->last_alloced_when;
201   }
202   allocator->diff += size;
203   GNUNET_CONTAINER_DLL_insert (ahead, atail, alloc);
204   print_allocators ();
205   bytes_alloced += size;
206 #endif
207 }
208
209
210 static void *
211 MEMDEBUG_malloc (size_t size, int line)
212 {
213   void * ret;
214
215   ret = GNUNET_malloc (size);
216 #if DEBUG_MALLOC
217   if (NULL != ret)
218       MEMDEBUG_add_alloc (ret, size, line);
219 #endif
220   return ret;
221
222 }
223
224 static void
225 MEMDEBUG_free (void * alloc, int line)
226 {
227 #if DEBUG_MALLOC
228   struct Allocation *cur;
229   struct Allocator *allocator;
230   cur = ahead;
231   while (NULL != cur)
232   {
233       if (alloc == cur->p)
234         break;
235       cur = cur->next;
236   }
237   if (NULL == cur)
238   {
239     fprintf (stderr, "Unmonitored free from line %4u\n", line);
240     GNUNET_break (0);
241     return;
242   }
243   allocator = cur->alloc;
244   if (NULL == allocator)
245   {
246       GNUNET_break (0);
247   }
248   GNUNET_CONTAINER_DLL_remove (ahead, atail, cur);
249   allocator->bytes_alloced -= cur->bytes_alloced;
250   allocator->diff -= cur->bytes_alloced;
251   GNUNET_assert (allocator->bytes_alloced >= 0);
252   bytes_alloced -= cur->bytes_alloced;
253   GNUNET_assert (bytes_alloced >= 0);
254   GNUNET_free (cur);
255 #endif
256   GNUNET_free (alloc);
257 }
258
259 static void
260 MEMDEBUG_free_non_null (void * alloc, int line)
261 {
262   if (alloc != NULL)
263     MEMDEBUG_free (alloc, line);
264 }
265
266
267 GNUNET_NETWORK_STRUCT_BEGIN
268
269 /**
270  * Message a peer sends to another to indicate that it intends to
271  * setup a connection/session for data exchange.  A 'SESSION_CONNECT'
272  * should be answered with a 'SESSION_CONNECT_ACK' with the same body
273  * to confirm.  A 'SESSION_CONNECT_ACK' should then be followed with
274  * a 'SESSION_ACK'.  Once the 'SESSION_ACK' is received, both peers 
275  * should be connected.
276  */
277 struct SessionConnectMessage
278 {
279   /**
280    * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT'
281    * or 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK'
282    */
283   struct GNUNET_MessageHeader header;
284
285   /**
286    * Always zero.
287    */
288   uint32_t reserved GNUNET_PACKED;
289
290   /**
291    * Absolute time at the sender.  Only the most recent connect
292    * message implies which session is preferred by the sender.
293    */
294   struct GNUNET_TIME_AbsoluteNBO timestamp;
295
296 };
297
298
299 /**
300  * Message we send to the other peer to notify him that we intentionally
301  * are disconnecting (to reduce timeouts).  This is just a friendly 
302  * notification, peers must not rely on always receiving disconnect
303  * messages.
304  */
305 struct SessionDisconnectMessage
306 {
307   /**
308    * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT'
309    */
310   struct GNUNET_MessageHeader header;
311
312   /**
313    * Always zero.
314    */
315   uint32_t reserved GNUNET_PACKED;
316
317   /**
318    * Purpose of the signature.  Extends over the timestamp.
319    * Purpose should be GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
320    */
321   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
322
323   /**
324    * Absolute time at the sender.  Only the most recent connect
325    * message implies which session is preferred by the sender.
326    */
327   struct GNUNET_TIME_AbsoluteNBO timestamp;
328
329   /**
330    * Public key of the sender.
331    */
332   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
333
334   /**
335    * Signature of the peer that sends us the disconnect.  Only
336    * valid if the timestamp is AFTER the timestamp from the
337    * corresponding 'CONNECT' message.
338    */
339   struct GNUNET_CRYPTO_EccSignature signature;
340
341 };
342
343 GNUNET_NETWORK_STRUCT_END
344
345
346 /**
347  * For each neighbour we keep a list of messages
348  * that we still want to transmit to the neighbour.
349  */
350 struct MessageQueue
351 {
352
353   /**
354    * This is a doubly linked list.
355    */
356   struct MessageQueue *next;
357
358   /**
359    * This is a doubly linked list.
360    */
361   struct MessageQueue *prev;
362
363   /**
364    * Function to call once we're done.
365    */
366   GST_NeighbourSendContinuation cont;
367
368   /**
369    * Closure for 'cont'
370    */
371   void *cont_cls;
372
373   /**
374    * The message(s) we want to transmit, GNUNET_MessageHeader(s)
375    * stuck together in memory.  Allocated at the end of this struct.
376    */
377   const char *message_buf;
378
379   /**
380    * Size of the message buf
381    */
382   size_t message_buf_size;
383
384   /**
385    * At what time should we fail?
386    */
387   struct GNUNET_TIME_Absolute timeout;
388
389 };
390
391
392 /**
393  * Possible state of a neighbour.  Initially, we are S_NOT_CONNECTED.
394  *
395  * Then, there are two main paths. If we receive a CONNECT message, we
396  * first run a check against the blacklist (S_CONNECT_RECV_BLACKLIST_INBOUND).
397  * If this check is successful, we give the inbound address to ATS.
398  * After the check we ask ATS for a suggestion (S_CONNECT_RECV_ATS).
399  * If ATS makes a suggestion, we ALSO give that suggestion to the blacklist
400  * (S_CONNECT_RECV_BLACKLIST).  Once the blacklist approves the
401  * address we got from ATS, we send our CONNECT_ACK and go to
402  * S_CONNECT_RECV_ACK.  If we receive a SESSION_ACK, we go to
403  * S_CONNECTED (and notify everyone about the new connection).  If the
404  * operation times out, we go to S_DISCONNECT.
405  *
406  * The other case is where we transmit a CONNECT message first.  We
407  * start with S_INIT_ATS.  If we get an address, we enter
408  * S_INIT_BLACKLIST and check the blacklist.  If the blacklist is OK
409  * with the connection, we actually send the CONNECT message and go to
410  * state S_CONNECT_SENT.  Once we receive a CONNECT_ACK, we go to
411  * S_CONNECTED (and notify everyone about the new connection and send
412  * back a SESSION_ACK).  If the operation times out, we go to
413  * S_DISCONNECT.
414  *
415  * If the session is in trouble (i.e. transport-level disconnect or
416  * timeout), we go to S_RECONNECT_ATS where we ask ATS for a new
417  * address (we don't notify anyone about the disconnect yet).  Once we
418  * have a new address, we go to S_RECONNECT_BLACKLIST to check the new
419  * address against the blacklist.  If the blacklist approves, we enter
420  * S_RECONNECT_SENT and send a CONNECT message.  If we receive a
421  * CONNECT_ACK, we go to S_CONNECTED and nobody noticed that we had
422  * trouble; we also send a SESSION_ACK at this time just in case.  If
423  * the operation times out, we go to S_DISCONNECT (and notify everyone
424  * about the lost connection).
425  *
426  * If ATS decides to switch addresses while we have a normal
427  * connection, we go to S_CONNECTED_SWITCHING_BLACKLIST to check the
428  * new address against the blacklist.  If the blacklist approves, we
429  * go to S_CONNECTED_SWITCHING_CONNECT_SENT and send a
430  * SESSION_CONNECT.  If we get a SESSION_ACK back, we switch the
431  * primary connection to the suggested alternative from ATS, go back
432  * to S_CONNECTED and send a SESSION_ACK to the other peer just to be
433  * sure.  If the operation times out (or the blacklist disapproves),
434  * we go to S_CONNECTED (and notify ATS that the given alternative
435  * address is "invalid").
436  *
437  * Once a session is in S_DISCONNECT, it is cleaned up and then goes
438  * to (S_DISCONNECT_FINISHED).  If we receive an explicit disconnect
439  * request, we can go from any state to S_DISCONNECT, possibly after
440  * generating disconnect notifications.
441  *
442  * Note that it is quite possible that while we are in any of these
443  * states, we could receive a 'CONNECT' request from the other peer.
444  * We then enter a 'weird' state where we pursue our own primary state
445  * machine (as described above), but with the 'send_connect_ack' flag
446  * set to 1.  If our state machine allows us to send a 'CONNECT_ACK'
447  * (because we have an acceptable address), we send the 'CONNECT_ACK'
448  * and set the 'send_connect_ack' to 2.  If we then receive a
449  * 'SESSION_ACK', we go to 'S_CONNECTED' (and reset 'send_connect_ack'
450  * to 0).
451  * 
452  */ 
453 enum State
454 {
455   /**
456    * fresh peer or completely disconnected
457    */
458   S_NOT_CONNECTED = 0,
459
460   /**
461    * Asked to initiate connection, trying to get address from ATS
462    */
463   S_INIT_ATS,
464
465   /**
466    * Asked to initiate connection, trying to get address approved
467    * by blacklist.
468    */
469   S_INIT_BLACKLIST,
470
471   /**
472    * Sent CONNECT message to other peer, waiting for CONNECT_ACK
473    */
474   S_CONNECT_SENT,
475
476   /**
477    * Received a CONNECT, do a blacklist check for inbound address
478    */
479   S_CONNECT_RECV_BLACKLIST_INBOUND,
480
481   /**
482    * Received a CONNECT, asking ATS about address suggestions.
483    */
484   S_CONNECT_RECV_ATS,
485
486   /**
487    * Received CONNECT from other peer, got an address, checking with blacklist.
488    */
489   S_CONNECT_RECV_BLACKLIST,
490
491   /**
492    * CONNECT request from other peer was SESSION_ACK'ed, waiting for
493    * SESSION_ACK.
494    */
495   S_CONNECT_RECV_ACK,
496
497   /**
498    * Got our CONNECT_ACK/SESSION_ACK, connection is up.
499    */
500   S_CONNECTED,
501
502   /**
503    * Connection got into trouble, rest of the system still believes
504    * it to be up, but we're getting a new address from ATS.
505    */
506   S_RECONNECT_ATS,
507
508   /**
509    * Connection got into trouble, rest of the system still believes
510    * it to be up; we are checking the new address against the blacklist.
511    */
512   S_RECONNECT_BLACKLIST,
513
514   /**
515    * Sent CONNECT over new address (either by ATS telling us to switch
516    * addresses or from RECONNECT_ATS); if this fails, we need to tell
517    * the rest of the system about a disconnect.
518    */
519   S_RECONNECT_SENT,
520
521   /**
522    * We have some primary connection, but ATS suggested we switch
523    * to some alternative; we're now checking the alternative against
524    * the blacklist.
525    */
526   S_CONNECTED_SWITCHING_BLACKLIST,
527
528   /** 
529    * We have some primary connection, but ATS suggested we switch
530    * to some alternative; we now sent a CONNECT message for the
531    * alternative session to the other peer and waiting for a
532    * CONNECT_ACK to make this our primary connection.
533    */
534   S_CONNECTED_SWITCHING_CONNECT_SENT,
535
536   /**
537    * Disconnect in progress (we're sending the DISCONNECT message to the
538    * other peer; after that is finished, the state will be cleaned up).
539    */
540   S_DISCONNECT,
541
542   /**
543    * We're finished with the disconnect; and are cleaning up the state
544    * now!  We put the struct into this state when we are really in the
545    * task that calls 'free' on it and are about to remove the record
546    * from the map.  We should never find a 'struct NeighbourMapEntry'
547    * in this state in the map.  Accessing a 'struct NeighbourMapEntry'
548    * in this state virtually always means using memory that has been
549    * freed (the exception being the cleanup code in 'free_neighbour').
550    */
551   S_DISCONNECT_FINISHED
552 };
553
554
555 /**
556  * A possible address we could use to communicate with a neighbour.
557  */
558 struct NeighbourAddress
559 {
560
561   /**
562    * Active session for this address.
563    */
564   struct Session *session;
565
566   /**
567    * Network-level address information.
568    */
569   struct GNUNET_HELLO_Address *address;
570
571   /**
572    * Timestamp of the 'SESSION_CONNECT' message we sent to the other
573    * peer for this address.  Use to check that the ACK is in response
574    * to our most recent 'CONNECT'.
575    */
576   struct GNUNET_TIME_Absolute connect_timestamp;
577
578   /**
579    * Inbound bandwidth from ATS for this address.
580    */
581   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
582
583   /**
584    * Outbound bandwidth from ATS for this address.
585    */
586   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
587
588   /**
589    * Did we tell ATS that this is our 'active' address?
590    */
591   int ats_active;
592   
593 };
594
595
596 /**
597  * Entry in neighbours.
598  */
599 struct NeighbourMapEntry
600 {
601
602   /**
603    * Head of list of messages we would like to send to this peer;
604    * must contain at most one message per client.
605    */
606   struct MessageQueue *messages_head;
607
608   /**
609    * Tail of list of messages we would like to send to this peer; must
610    * contain at most one message per client.
611    */
612   struct MessageQueue *messages_tail;
613
614   /**
615    * Are we currently trying to send a message? If so, which one?
616    */
617   struct MessageQueue *is_active;
618
619   /**
620    * Primary address we currently use to communicate with the neighbour.
621    */
622   struct NeighbourAddress primary_address;
623
624   /**
625    * Alternative address currently under consideration for communicating
626    * with the neighbour.
627    */
628   struct NeighbourAddress alternative_address;
629
630   /**
631    * Identity of this neighbour.
632    */
633   struct GNUNET_PeerIdentity id;
634
635   /**
636    * Main task that drives this peer (timeouts, keepalives, etc.).
637    * Always runs the 'master_task'.
638    */
639   GNUNET_SCHEDULER_TaskIdentifier task;
640
641   /**
642    * At what time should we sent the next keep-alive message?
643    */
644   struct GNUNET_TIME_Absolute keep_alive_time;
645
646   /**
647    * At what time did we sent the last keep-alive message?  Used 
648    * to calculate round-trip time ("latency").
649    */
650   struct GNUNET_TIME_Absolute last_keep_alive_time;
651
652   /**
653    * Timestamp we should include in our next CONNECT_ACK message.
654    * (only valid if 'send_connect_ack' is GNUNET_YES).  Used to build
655    * our CONNECT_ACK message.
656    */
657   struct GNUNET_TIME_Absolute connect_ack_timestamp;
658
659   /**
660    * ATS address suggest handle
661    */
662   struct GNUNET_ATS_SuggestHandle *suggest_handle;
663
664   /**
665    * Time where we should cut the connection (timeout) if we don't
666    * make progress in the state machine (or get a KEEPALIVE_RESPONSE
667    * if we are in S_CONNECTED).
668    */
669   struct GNUNET_TIME_Absolute timeout;
670
671   /**
672    * Latest calculated latency value
673    */
674   struct GNUNET_TIME_Relative latency;
675
676   /**
677    * Tracker for inbound bandwidth.
678    */
679   struct GNUNET_BANDWIDTH_Tracker in_tracker;
680
681   /**
682    * How often has the other peer (recently) violated the inbound
683    * traffic limit?  Incremented by 10 per violation, decremented by 1
684    * per non-violation (for each time interval).
685    */
686   unsigned int quota_violation_count;
687
688   /**
689    * The current state of the peer.
690    */
691   enum State state;
692
693   /**
694    * Did we sent an KEEP_ALIVE message and are we expecting a response?
695    */
696   int expect_latency_response;
697
698   /**
699    * Flag to set if we still need to send a CONNECT_ACK message to the other peer
700    * (once we have an address to use and the peer has been allowed by our
701    * blacklist).  Set to 1 if we need to send a CONNECT_ACK.  Set to 2 if we
702    * did send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving
703    * a 'SESSION_ACK' (regardless of what our own state machine might say).
704    */
705   int send_connect_ack;
706
707 };
708
709
710 /**
711  * Context for blacklist checks and the 'handle_test_blacklist_cont'
712  * function.  Stores information about ongoing blacklist checks.
713  */
714 struct BlackListCheckContext
715 {
716   
717   /**
718    * We keep blacklist checks in a DLL.
719    */
720   struct BlackListCheckContext *next;
721
722   /**
723    * We keep blacklist checks in a DLL.
724    */
725   struct BlackListCheckContext *prev;
726
727   /**
728    * Address that is being checked.
729    */
730   struct NeighbourAddress na;
731   
732   /**
733    * ATS information about the address.
734    */
735   struct GNUNET_ATS_Information *ats;
736
737   /**
738    * Handle to the ongoing blacklist check.
739    */
740   struct GST_BlacklistCheck *bc;
741
742   /**
743    * Size of the 'ats' array.
744    */
745   uint32_t ats_count;
746
747 };
748
749
750 /**
751  * Hash map from peer identities to the respective 'struct NeighbourMapEntry'.
752  */
753 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
754
755 /**
756  * We keep blacklist checks in a DLL so that we can find
757  * the 'sessions' in their 'struct NeighbourAddress' if
758  * a session goes down.
759  */
760 static struct BlackListCheckContext *bc_head;
761
762 /**
763  * We keep blacklist checks in a DLL.
764  */
765 static struct BlackListCheckContext *bc_tail;
766
767 /**
768  * Closure for connect_notify_cb, disconnect_notify_cb and address_change_cb
769  */
770 static void *callback_cls;
771
772 /**
773  * Function to call when we connected to a neighbour.
774  */
775 static NotifyConnect connect_notify_cb;
776
777 /**
778  * Function to call when we disconnected from a neighbour.
779  */
780 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
781
782 /**
783  * Function to call when we changed an active address of a neighbour.
784  */
785 static GNUNET_TRANSPORT_PeerIterateCallback address_change_cb;
786
787 /**
788  * counter for connected neighbours
789  */
790 static unsigned int neighbours_connected;
791
792 /**
793  * Number of bytes we have currently queued for transmission.
794  */
795 static unsigned long long bytes_in_send_queue;
796
797
798 /**
799  * Lookup a neighbour entry in the neighbours hash map.
800  *
801  * @param pid identity of the peer to look up
802  * @return the entry, NULL if there is no existing record
803  */
804 static struct NeighbourMapEntry *
805 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
806 {
807   if (NULL == neighbours)
808     return NULL;
809   return GNUNET_CONTAINER_multihashmap_get (neighbours, &pid->hashPubKey);
810 }
811
812 static const char *
813 print_state (int state)
814 {
815
816   switch (state)
817   {
818   case S_NOT_CONNECTED:
819     return "S_NOT_CONNECTED";
820   case S_INIT_ATS:
821     return "S_INIT_ATS";
822   case S_INIT_BLACKLIST:
823     return "S_INIT_BLACKLIST";
824   case S_CONNECT_SENT:
825     return "S_CONNECT_SENT";
826   case S_CONNECT_RECV_BLACKLIST_INBOUND:
827     return "S_CONNECT_RECV_BLACKLIST_INBOUND";
828   case S_CONNECT_RECV_ATS:
829     return "S_CONNECT_RECV_ATS";
830   case S_CONNECT_RECV_BLACKLIST:
831     return "S_CONNECT_RECV_BLACKLIST";
832   case S_CONNECT_RECV_ACK:
833     return "S_CONNECT_RECV_ACK";
834   case S_CONNECTED:
835     return "S_CONNECTED";
836   case S_RECONNECT_ATS:
837     return "S_RECONNECT_ATS";
838   case S_RECONNECT_BLACKLIST:
839     return "S_RECONNECT_BLACKLIST";
840   case S_RECONNECT_SENT:
841     return "S_RECONNECT_SENT";
842   case S_CONNECTED_SWITCHING_BLACKLIST:
843     return "S_CONNECTED_SWITCHING_BLACKLIST";
844   case S_CONNECTED_SWITCHING_CONNECT_SENT:
845     return "S_CONNECTED_SWITCHING_CONNECT_SENT";
846   case S_DISCONNECT:
847     return "S_DISCONNECT";
848   case S_DISCONNECT_FINISHED:
849     return "S_DISCONNECT_FINISHED";
850   default:
851     GNUNET_break (0);
852     return "UNDEFINED";
853   }
854 }
855
856 /**
857  * Test if we're connected to the given peer.
858  *
859  * @param n neighbour entry of peer to test
860  * @return GNUNET_YES if we are connected, GNUNET_NO if not
861  */
862 static int
863 test_connected (struct NeighbourMapEntry *n)
864 {
865   if (NULL == n)
866     return GNUNET_NO;
867   switch (n->state)
868   {
869   case S_NOT_CONNECTED:
870   case S_INIT_ATS:
871   case S_INIT_BLACKLIST:
872   case S_CONNECT_SENT:
873   case S_CONNECT_RECV_BLACKLIST_INBOUND:
874   case S_CONNECT_RECV_ATS:
875   case S_CONNECT_RECV_BLACKLIST:
876   case S_CONNECT_RECV_ACK:
877     return GNUNET_NO;
878   case S_CONNECTED:
879   case S_RECONNECT_ATS:
880   case S_RECONNECT_BLACKLIST:
881   case S_RECONNECT_SENT:
882   case S_CONNECTED_SWITCHING_BLACKLIST:
883   case S_CONNECTED_SWITCHING_CONNECT_SENT:
884     return GNUNET_YES;
885   case S_DISCONNECT:
886   case S_DISCONNECT_FINISHED:
887     return GNUNET_NO;
888   default:
889     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
890     GNUNET_break (0);
891     break;
892   }
893   return GNUNET_SYSERR;
894 }
895
896 /**
897  * Send information about a new outbound quota to our clients.
898  *
899  * @param target affected peer
900  * @param quota new quota
901  */
902 static void
903 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
904                      struct GNUNET_BANDWIDTH_Value32NBO quota)
905 {
906   struct QuotaSetMessage q_msg;
907
908   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909               "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
910               ntohl (quota.value__), GNUNET_i2s (target));
911   q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
912   q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
913   q_msg.quota = quota;
914   q_msg.peer = (*target);
915   GST_clients_broadcast (&q_msg.header, GNUNET_NO);
916 }
917
918
919 /**
920  * We don't need a given neighbour address any more.
921  * Release its resources and give appropriate notifications
922  * to ATS and other subsystems.
923  *
924  * @param na address we are done with; 'na' itself must NOT be 'free'd, only the contents!
925  */
926 static void
927 free_address (struct NeighbourAddress *na)
928 {
929   if (GNUNET_YES == na->ats_active)
930   {
931     GST_validation_set_address_use (na->address, na->session, GNUNET_NO, __LINE__);
932     GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
933     address_change_cb (NULL, &na->address->peer, NULL);
934   }
935
936   na->ats_active = GNUNET_NO;
937   if (NULL != na->address)
938   {
939     MEMDEBUG_free (na->address, __LINE__);
940     //GNUNET_HELLO_address_free (na->address);
941     na->address = NULL;
942   }
943   na->session = NULL;
944 }
945
946
947 /**
948  * Initialize the 'struct NeighbourAddress'.
949  *
950  * @param na neighbour address to initialize
951  * @param address address of the other peer, NULL if other peer
952  *                       connected to us
953  * @param session session to use (or NULL, in which case an
954  *        address must be setup)
955  * @param bandwidth_in inbound quota to be used when connection is up
956  * @param bandwidth_out outbound quota to be used when connection is up
957  * @param is_active GNUNET_YES to mark this as the active address with ATS
958  */
959 static void
960 set_address (struct NeighbourAddress *na,
961              const struct GNUNET_HELLO_Address *address,
962              struct Session *session,
963              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
964              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
965              int is_active)
966 {
967   struct GNUNET_TRANSPORT_PluginFunctions *papi;
968   if (NULL == (papi = GST_plugins_find (address->transport_name)))  
969   {
970     GNUNET_break (0);
971     return;
972   }
973   if (session == na->session)
974   {
975     na->bandwidth_in = bandwidth_in;
976     na->bandwidth_out = bandwidth_out;
977     if (is_active != na->ats_active)
978     {
979       na->ats_active = is_active;
980       GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, is_active);
981       GST_validation_set_address_use (na->address, na->session, is_active,  __LINE__);
982       if (is_active)
983         address_change_cb (NULL, &address->peer, address);
984     }
985     if (GNUNET_YES == is_active)
986     {
987       /* FIXME: is this the right place to set quotas? */
988       GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
989       send_outbound_quota (&address->peer, bandwidth_out);
990     }    
991     return;
992   }
993   free_address (na);
994   if (NULL == session)
995     session = papi->get_session (papi->cls, address);    
996   if (NULL == session)
997   {
998     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
999                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
1000                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));    
1001     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
1002     return;
1003   }
1004   na->address = GNUNET_HELLO_address_copy (address);
1005   MEMDEBUG_add_alloc (na->address, GNUNET_HELLO_address_get_size (na->address), __LINE__);
1006   na->bandwidth_in = bandwidth_in;
1007   na->bandwidth_out = bandwidth_out;
1008   na->session = session;
1009   na->ats_active = is_active;
1010   if (GNUNET_YES == is_active)
1011   {
1012     /* Telling ATS about new session */
1013     GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_YES);
1014     GST_validation_set_address_use (na->address, na->session, GNUNET_YES,  __LINE__);
1015     address_change_cb (NULL, &address->peer, address);
1016     /* FIXME: is this the right place to set quotas? */
1017     GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
1018     send_outbound_quota (&address->peer, bandwidth_out);
1019   }
1020 }
1021
1022
1023 /**
1024  * Free a neighbour map entry.
1025  *
1026  * @param n entry to free
1027  * @param keep_sessions GNUNET_NO to tell plugin to terminate sessions,
1028  *                      GNUNET_YES to keep all sessions
1029  */
1030 static void
1031 free_neighbour (struct NeighbourMapEntry *n, int keep_sessions)
1032 {
1033   struct MessageQueue *mq;
1034   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1035   struct GNUNET_HELLO_Address *backup_primary;
1036
1037   n->is_active = NULL; /* always free'd by its own continuation! */
1038
1039   /* fail messages currently in the queue */
1040   while (NULL != (mq = n->messages_head))
1041   {
1042     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1043     if (NULL != mq->cont)
1044       mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
1045     MEMDEBUG_free (mq, __LINE__);
1046   }
1047   /* It is too late to send other peer disconnect notifications, but at
1048      least internally we need to get clean... */
1049   if (GNUNET_YES == test_connected (n))
1050   {
1051     GNUNET_STATISTICS_set (GST_stats, 
1052                            gettext_noop ("# peers connected"), 
1053                            --neighbours_connected,
1054                            GNUNET_NO);
1055     disconnect_notify_cb (callback_cls, &n->id);
1056   }
1057
1058   n->state = S_DISCONNECT_FINISHED;
1059
1060   if (NULL != n->primary_address.address)
1061   {
1062     backup_primary = GNUNET_HELLO_address_copy(n->primary_address.address);
1063     MEMDEBUG_add_alloc (backup_primary, GNUNET_HELLO_address_get_size(backup_primary), __LINE__);
1064   }
1065   else
1066     backup_primary = NULL;
1067
1068   /* free addresses and mark as unused */
1069   free_address (&n->primary_address);
1070   free_address (&n->alternative_address);
1071
1072   /* FIXME-PLUGIN-API: This does not seem to guarantee that all
1073      transport sessions eventually get killed due to inactivity; they
1074      MUST have their own timeout logic (but at least TCP doesn't have
1075      one yet).  Are we sure that EVERY 'session' of a plugin is
1076      actually cleaned up this way!?  Note that if we are switching
1077      between two TCP sessions to the same peer, the existing plugin
1078      API gives us not even the means to selectively kill only one of
1079      them! Killing all sessions like this seems to be very, very
1080      wrong. */
1081
1082   /* cut transport-level connection */
1083   if ((GNUNET_NO == keep_sessions) &&
1084       (NULL != backup_primary) &&
1085       (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
1086     papi->disconnect (papi->cls, &n->id);
1087
1088   MEMDEBUG_free_non_null (backup_primary, __LINE__);
1089
1090   GNUNET_assert (GNUNET_YES ==
1091                  GNUNET_CONTAINER_multihashmap_remove (neighbours,
1092                                                        &n->id.hashPubKey, n));
1093
1094   // FIXME-ATS-API: we might want to be more specific about
1095   // which states we do this from in the future (ATS should
1096   // have given us a 'suggest_address' handle, and if we have
1097   // such a handle, we should cancel the operation here!
1098   if (NULL != n->suggest_handle)
1099   {
1100         GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
1101         n->suggest_handle = NULL;
1102   }
1103
1104   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1105   {
1106     GNUNET_SCHEDULER_cancel (n->task);
1107     n->task = GNUNET_SCHEDULER_NO_TASK;
1108   }
1109   /* free rest of memory */
1110   MEMDEBUG_free (n, __LINE__);
1111 }
1112
1113 /**
1114  * Transmit a message using the current session of the given
1115  * neighbour.
1116  *
1117  * @param n entry for the recipient
1118  * @param msgbuf buffer to transmit
1119  * @param msgbuf_size number of bytes in buffer
1120  * @param priority transmission priority
1121  * @param timeout transmission timeout
1122  * @param cont continuation to call when finished (can be NULL)
1123  * @param cont_cls closure for cont
1124  */
1125 static void
1126 send_with_session (struct NeighbourMapEntry *n,
1127                    const char *msgbuf, size_t msgbuf_size,
1128                    uint32_t priority,
1129                    struct GNUNET_TIME_Relative timeout,
1130                    GNUNET_TRANSPORT_TransmitContinuation cont,
1131                    void *cont_cls)
1132 {
1133   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1134
1135   GNUNET_assert (n->primary_address.session != NULL);
1136   if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
1137          (-1 == papi->send (papi->cls,
1138                             n->primary_address.session,
1139                             msgbuf, msgbuf_size,
1140                             priority,
1141                             timeout,
1142                             cont, cont_cls)))) &&
1143        (NULL != cont))
1144     cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
1145   GNUNET_break (NULL != papi);
1146 }
1147
1148
1149 /**
1150  * Master task run for every neighbour.  Performs all of the time-related
1151  * activities (keep alive, send next message, disconnect if idle, finish
1152  * clean up after disconnect).
1153  *
1154  * @param cls the 'struct NeighbourMapEntry' for which we are running
1155  * @param tc scheduler context (unused)
1156  */
1157 static void
1158 master_task (void *cls,
1159              const struct GNUNET_SCHEDULER_TaskContext *tc);
1160
1161
1162 /**
1163  * Function called when the 'DISCONNECT' message has been sent by the
1164  * plugin.  Frees the neighbour --- if the entry still exists.
1165  *
1166  * @param cls NULL
1167  * @param target identity of the neighbour that was disconnected
1168  * @param result GNUNET_OK if the disconnect got out successfully
1169  * @param payload bytes payload
1170  * @param physical bytes physical
1171  */
1172 static void
1173 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
1174                       int result, size_t payload, size_t physical)
1175 {
1176   struct NeighbourMapEntry *n;
1177
1178   n = lookup_neighbour (target);
1179   if (NULL == n)
1180     return; /* already gone */
1181   if (S_DISCONNECT != n->state)
1182     return; /* have created a fresh entry since */
1183   n->state = S_DISCONNECT;
1184   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1185     GNUNET_SCHEDULER_cancel (n->task);
1186   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1187 }
1188
1189
1190 /**
1191  * Transmit a DISCONNECT message to the other peer.
1192  *
1193  * @param n neighbour to send DISCONNECT message.
1194  */
1195 static void
1196 send_disconnect (struct NeighbourMapEntry *n)
1197 {
1198   struct SessionDisconnectMessage disconnect_msg;
1199
1200   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1201               "Sending DISCONNECT message to peer `%4s'\n",
1202               GNUNET_i2s (&n->id));
1203   disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1204   disconnect_msg.header.type =
1205       htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1206   disconnect_msg.reserved = htonl (0);
1207   disconnect_msg.purpose.size =
1208       htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1209              sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
1210              sizeof (struct GNUNET_TIME_AbsoluteNBO));
1211   disconnect_msg.purpose.purpose =
1212       htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1213   disconnect_msg.timestamp =
1214       GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1215   disconnect_msg.public_key = GST_my_public_key;
1216   GNUNET_assert (GNUNET_OK ==
1217                  GNUNET_CRYPTO_ecc_sign (GST_my_private_key,
1218                                          &disconnect_msg.purpose,
1219                                          &disconnect_msg.signature));
1220
1221   send_with_session (n,
1222                      (const char *) &disconnect_msg, sizeof (disconnect_msg),
1223                      UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1224                      &send_disconnect_cont, NULL);
1225   GNUNET_STATISTICS_update (GST_stats,
1226                             gettext_noop
1227                             ("# DISCONNECT messages sent"), 1,
1228                             GNUNET_NO);
1229 }
1230
1231
1232 /**
1233  * Disconnect from the given neighbour, clean up the record.
1234  *
1235  * @param n neighbour to disconnect from
1236  */
1237 static void
1238 disconnect_neighbour (struct NeighbourMapEntry *n)
1239 {
1240   /* depending on state, notify neighbour and/or upper layers of this peer 
1241      about disconnect */
1242   switch (n->state)
1243   {
1244   case S_NOT_CONNECTED:
1245   case S_INIT_ATS:
1246   case S_INIT_BLACKLIST:
1247     /* other peer is completely unaware of us, no need to send DISCONNECT */
1248     n->state = S_DISCONNECT_FINISHED;
1249     free_neighbour (n, GNUNET_NO);
1250     return;
1251   case S_CONNECT_SENT:
1252     send_disconnect (n); 
1253     n->state = S_DISCONNECT;
1254     break;
1255   case S_CONNECT_RECV_BLACKLIST_INBOUND:
1256   case S_CONNECT_RECV_ATS:
1257   case S_CONNECT_RECV_BLACKLIST:
1258     /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1259     n->state = S_DISCONNECT_FINISHED;
1260     free_neighbour (n, GNUNET_NO);
1261     return;
1262   case S_CONNECT_RECV_ACK:
1263     /* we DID ACK the other peer's request, must send DISCONNECT */
1264     send_disconnect (n); 
1265     n->state = S_DISCONNECT;
1266     break;   
1267   case S_CONNECTED:
1268   case S_RECONNECT_BLACKLIST:
1269   case S_RECONNECT_SENT:
1270   case S_CONNECTED_SWITCHING_BLACKLIST:
1271   case S_CONNECTED_SWITCHING_CONNECT_SENT:
1272     /* we are currently connected, need to send disconnect and do
1273        internal notifications and update statistics */
1274     send_disconnect (n);
1275     GNUNET_STATISTICS_set (GST_stats, 
1276                            gettext_noop ("# peers connected"), 
1277                            --neighbours_connected,
1278                            GNUNET_NO);
1279     disconnect_notify_cb (callback_cls, &n->id);
1280     n->state = S_DISCONNECT;
1281     break;
1282   case S_RECONNECT_ATS:
1283     /* ATS address request timeout, disconnect without sending disconnect message */
1284     GNUNET_STATISTICS_set (GST_stats,
1285                            gettext_noop ("# peers connected"),
1286                            --neighbours_connected,
1287                            GNUNET_NO);
1288     disconnect_notify_cb (callback_cls, &n->id);
1289     n->state = S_DISCONNECT;
1290     break;
1291   case S_DISCONNECT:
1292     /* already disconnected, ignore */
1293     break;
1294   case S_DISCONNECT_FINISHED:
1295     /* already cleaned up, how did we get here!? */
1296     GNUNET_assert (0);
1297     break;
1298   default:
1299     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
1300     GNUNET_break (0);
1301     break;
1302   }
1303   /* schedule timeout to clean up */
1304   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1305     GNUNET_SCHEDULER_cancel (n->task);
1306   n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1307                                           &master_task, n);
1308 }
1309
1310
1311 /**
1312  * We're done with our transmission attempt, continue processing.
1313  *
1314  * @param cls the 'struct MessageQueue' of the message
1315  * @param receiver intended receiver
1316  * @param success whether it worked or not
1317  * @param size_payload bytes payload sent
1318  * @param physical bytes sent on wire
1319  */
1320 static void
1321 transmit_send_continuation (void *cls,
1322                             const struct GNUNET_PeerIdentity *receiver,
1323                             int success, size_t size_payload, size_t physical)
1324 {
1325   struct MessageQueue *mq = cls;
1326   struct NeighbourMapEntry *n;
1327
1328   if (NULL == (n = lookup_neighbour (receiver)))
1329   {
1330     MEMDEBUG_free (mq, __LINE__);
1331     return; /* disconnect or other error while transmitting, can happen */
1332   }
1333   if (n->is_active == mq)
1334   {
1335     /* this is still "our" neighbour, remove us from its queue
1336        and allow it to send the next message now */
1337     n->is_active = NULL;
1338     if (GNUNET_SCHEDULER_NO_TASK != n->task)
1339       GNUNET_SCHEDULER_cancel (n->task);
1340     n->task = GNUNET_SCHEDULER_add_now (&master_task, n);    
1341   }
1342   if (bytes_in_send_queue < mq->message_buf_size)
1343   {
1344       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1345                   "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1346                   bytes_in_send_queue, mq->message_buf_size,
1347                   (GNUNET_OK == success) ? "OK" : "FAIL",
1348                   size_payload, physical);
1349       GNUNET_break (0);
1350   }
1351
1352
1353   GNUNET_break (size_payload == mq->message_buf_size);
1354   bytes_in_send_queue -= mq->message_buf_size;
1355   GNUNET_STATISTICS_set (GST_stats,
1356                         gettext_noop
1357                          ("# bytes in message queue for other peers"),
1358                          bytes_in_send_queue, GNUNET_NO);
1359   if (GNUNET_OK == success)
1360     GNUNET_STATISTICS_update (GST_stats,
1361                               gettext_noop
1362                               ("# messages transmitted to other peers"),
1363                               1, GNUNET_NO);
1364   else
1365     GNUNET_STATISTICS_update (GST_stats,
1366                               gettext_noop
1367                               ("# transmission failures for messages to other peers"),
1368                               1, GNUNET_NO);
1369   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
1370               "Sending message to `%s' of type %u was a %s\n",
1371               GNUNET_i2s (receiver),
1372               ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1373               (success == GNUNET_OK) ? "success" : "FAILURE");
1374   if (NULL != mq->cont)
1375     mq->cont (mq->cont_cls, success, size_payload, physical);
1376   MEMDEBUG_free (mq, __LINE__);
1377 }
1378
1379
1380 /**
1381  * Check the message list for the given neighbour and if we can
1382  * send a message, do so.  This function should only be called
1383  * if the connection is at least generally ready for transmission.
1384  * While we will only send one message at a time, no bandwidth
1385  * quota management is performed here.  If a message was given to
1386  * the plugin, the continuation will automatically re-schedule
1387  * the 'master' task once the next message might be transmitted.
1388  *
1389  * @param n target peer for which to transmit
1390  */
1391 static void
1392 try_transmission_to_peer (struct NeighbourMapEntry *n)
1393 {
1394   struct MessageQueue *mq;
1395   struct GNUNET_TIME_Relative timeout;
1396
1397   if (NULL == n->primary_address.address)
1398   {
1399     /* no address, why are we here? */
1400     GNUNET_break (0);
1401     return;
1402   }
1403   if ((0 == n->primary_address.address->address_length) && 
1404       (NULL == n->primary_address.session))
1405   {
1406     /* no address, why are we here? */
1407     GNUNET_break (0);
1408     return;
1409   }
1410   if (NULL != n->is_active)
1411   {
1412     /* transmission already pending */
1413     return;                     
1414   }
1415
1416   /* timeout messages from the queue that are past their due date */
1417   while (NULL != (mq = n->messages_head))
1418   {
1419     timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1420     if (timeout.rel_value > 0)
1421       break;
1422     GNUNET_STATISTICS_update (GST_stats,
1423                               gettext_noop
1424                               ("# messages timed out while in transport queue"),
1425                               1, GNUNET_NO);
1426     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1427     n->is_active = mq;
1428     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR, mq->message_buf_size, 0);     /* timeout */
1429   }
1430   if (NULL == mq)
1431     return;                     /* no more messages */
1432   GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1433   n->is_active = mq;
1434   send_with_session (n,
1435                      mq->message_buf, mq->message_buf_size,
1436                      0 /* priority */, timeout,
1437                      &transmit_send_continuation, mq);
1438 }
1439
1440
1441 /**
1442  * Send keepalive message to the neighbour.  Must only be called
1443  * if we are on 'connected' state or while trying to switch addresses.
1444  * Will internally determine if a keepalive is truly needed (so can
1445  * always be called).
1446  *
1447  * @param n neighbour that went idle and needs a keepalive
1448  */
1449 static void
1450 send_keepalive (struct NeighbourMapEntry *n)
1451 {
1452   struct GNUNET_MessageHeader m;
1453
1454   GNUNET_assert ((S_CONNECTED == n->state) ||
1455                  (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1456                  (S_CONNECTED_SWITCHING_CONNECT_SENT));
1457   if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value > 0)
1458     return; /* no keepalive needed at this time */
1459   m.size = htons (sizeof (struct GNUNET_MessageHeader));
1460   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1461   send_with_session (n,
1462                      (const void *) &m, sizeof (m),
1463                      UINT32_MAX /* priority */,
1464                      KEEPALIVE_FREQUENCY,
1465                      NULL, NULL);
1466   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1467                             GNUNET_NO);
1468   n->expect_latency_response = GNUNET_YES;
1469   n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1470   n->keep_alive_time = GNUNET_TIME_relative_to_absolute (KEEPALIVE_FREQUENCY);
1471 }
1472
1473
1474 /**
1475  * Keep the connection to the given neighbour alive longer,
1476  * we received a KEEPALIVE (or equivalent); send a response.
1477  *
1478  * @param neighbour neighbour to keep alive (by sending keep alive response)
1479  */
1480 void
1481 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour)
1482 {
1483   struct NeighbourMapEntry *n;
1484   struct GNUNET_MessageHeader m;
1485
1486   if (NULL == (n = lookup_neighbour (neighbour)))
1487   {
1488     GNUNET_STATISTICS_update (GST_stats,
1489                               gettext_noop
1490                               ("# KEEPALIVE messages discarded (peer unknown)"),
1491                               1, GNUNET_NO);
1492     return;
1493   }
1494   if (NULL == n->primary_address.session)
1495   {
1496     GNUNET_STATISTICS_update (GST_stats,
1497                               gettext_noop
1498                               ("# KEEPALIVE messages discarded (no session)"),
1499                               1, GNUNET_NO);
1500     return;
1501   }
1502   /* send reply to allow neighbour to measure latency */
1503   m.size = htons (sizeof (struct GNUNET_MessageHeader));
1504   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1505   send_with_session(n,
1506                     (const void *) &m, sizeof (m),
1507                     UINT32_MAX /* priority */,
1508                     KEEPALIVE_FREQUENCY,
1509                     NULL, NULL);
1510 }
1511
1512
1513 /**
1514  * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1515  * latency to this peer.  Pass the updated information (existing ats
1516  * plus calculated latency) to ATS.
1517  *
1518  * @param neighbour neighbour to keep alive
1519  * @param ats performance data
1520  * @param ats_count number of entries in ats
1521  */
1522 void
1523 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1524                                    const struct GNUNET_ATS_Information *ats,
1525                                    uint32_t ats_count)
1526 {
1527   struct NeighbourMapEntry *n;
1528   uint32_t latency;
1529   struct GNUNET_ATS_Information ats_new[ats_count + 1];
1530
1531   if (NULL == (n = lookup_neighbour (neighbour)))
1532   {
1533     GNUNET_STATISTICS_update (GST_stats,
1534                               gettext_noop
1535                               ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1536                               1, GNUNET_NO);
1537     return;
1538   }
1539   if ( (S_CONNECTED != n->state) ||
1540        (GNUNET_YES != n->expect_latency_response) )
1541   {
1542     GNUNET_STATISTICS_update (GST_stats,
1543                               gettext_noop
1544                               ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1545                               1, GNUNET_NO);
1546     return;
1547   }
1548   n->expect_latency_response = GNUNET_NO;
1549   n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1550   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1551   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1552               "Latency for peer `%s' is %llu ms\n",
1553               GNUNET_i2s (&n->id), n->latency.rel_value);
1554   memcpy (ats_new, ats, sizeof (struct GNUNET_ATS_Information) * ats_count);
1555   /* append latency */
1556   ats_new[ats_count].type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1557   if (n->latency.rel_value > UINT32_MAX)
1558     latency = UINT32_MAX;
1559   else
1560     latency = n->latency.rel_value;
1561   ats_new[ats_count].value = htonl (latency);
1562   GNUNET_ATS_address_update (GST_ats, 
1563                              n->primary_address.address, 
1564                              n->primary_address.session, ats_new,
1565                              ats_count + 1);
1566 }
1567
1568
1569 /**
1570  * We have received a message from the given sender.  How long should
1571  * we delay before receiving more?  (Also used to keep the peer marked
1572  * as live).
1573  *
1574  * @param sender sender of the message
1575  * @param size size of the message
1576  * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1577  *                   GNUNET_NO if the neighbour is not connected or violates the quota,
1578  *                   GNUNET_SYSERR if the connection is not fully up yet
1579  * @return how long to wait before reading more from this sender
1580  */
1581 struct GNUNET_TIME_Relative
1582 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1583                                         *sender, ssize_t size, int *do_forward)
1584 {
1585   struct NeighbourMapEntry *n;
1586   struct GNUNET_TIME_Relative ret;
1587   
1588   if (NULL == neighbours)
1589   {
1590     *do_forward = GNUNET_NO;
1591     return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1592   }
1593   if (NULL == (n = lookup_neighbour (sender)))
1594   {
1595     GST_neighbours_try_connect (sender);
1596     if (NULL == (n = lookup_neighbour (sender)))
1597     {
1598       GNUNET_STATISTICS_update (GST_stats,
1599                                 gettext_noop
1600                                 ("# messages discarded due to lack of neighbour record"),
1601                                 1, GNUNET_NO);
1602       *do_forward = GNUNET_NO;
1603       return GNUNET_TIME_UNIT_ZERO;
1604     }
1605   }
1606   if (! test_connected (n))
1607   {
1608     *do_forward = GNUNET_SYSERR;
1609     return GNUNET_TIME_UNIT_ZERO;
1610   }
1611   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1612   {
1613     n->quota_violation_count++;
1614     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1615                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1616                 n->in_tracker.available_bytes_per_s__,
1617                 n->quota_violation_count);
1618     /* Discount 32k per violation */
1619     GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1620   }
1621   else
1622   {
1623     if (n->quota_violation_count > 0)
1624     {
1625       /* try to add 32k back */
1626       GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1627       n->quota_violation_count--;
1628     }
1629   }
1630   if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1631   {
1632     GNUNET_STATISTICS_update (GST_stats,
1633                               gettext_noop
1634                               ("# bandwidth quota violations by other peers"),
1635                               1, GNUNET_NO);
1636     *do_forward = GNUNET_NO;
1637     return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1638   }
1639   *do_forward = GNUNET_YES;
1640   ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1641   if (ret.rel_value > 0)
1642   {
1643     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1644                 "Throttling read (%llu bytes excess at %u b/s), waiting %llu ms before reading more.\n",
1645                 (unsigned long long) n->in_tracker.
1646                 consumption_since_last_update__,
1647                 (unsigned int) n->in_tracker.available_bytes_per_s__,
1648                 (unsigned long long) ret.rel_value);
1649     GNUNET_STATISTICS_update (GST_stats,
1650                               gettext_noop ("# ms throttling suggested"),
1651                               (int64_t) ret.rel_value, GNUNET_NO);
1652   }
1653   return ret;
1654 }
1655
1656
1657 /**
1658  * Transmit a message to the given target using the active connection.
1659  *
1660  * @param target destination
1661  * @param msg message to send
1662  * @param msg_size number of bytes in msg
1663  * @param timeout when to fail with timeout
1664  * @param cont function to call when done
1665  * @param cont_cls closure for 'cont'
1666  */
1667 void
1668 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1669                      size_t msg_size, struct GNUNET_TIME_Relative timeout,
1670                      GST_NeighbourSendContinuation cont, void *cont_cls)
1671 {
1672   struct NeighbourMapEntry *n;
1673   struct MessageQueue *mq;
1674
1675   /* All ove these cases should never happen; they are all API violations.
1676      But we check anyway, just to be sure. */
1677   if (NULL == (n = lookup_neighbour (target)))
1678   {
1679     GNUNET_break (0);
1680     if (NULL != cont)
1681       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1682     return;
1683   }
1684   if (GNUNET_YES != test_connected (n))
1685   {
1686     GNUNET_break (0);
1687     if (NULL != cont)
1688       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1689     return;
1690   }
1691   bytes_in_send_queue += msg_size;
1692   GNUNET_STATISTICS_set (GST_stats,
1693                          gettext_noop
1694                          ("# bytes in message queue for other peers"),
1695                          bytes_in_send_queue, GNUNET_NO);
1696   mq = MEMDEBUG_malloc (sizeof (struct MessageQueue) + msg_size, __LINE__);
1697   mq->cont = cont;
1698   mq->cont_cls = cont_cls;
1699   memcpy (&mq[1], msg, msg_size);
1700   mq->message_buf = (const char *) &mq[1];
1701   mq->message_buf_size = msg_size;
1702   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1703   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1704   if ( (NULL != n->is_active) ||
1705        ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1706     return;
1707   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1708     GNUNET_SCHEDULER_cancel (n->task);
1709   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1710 }
1711
1712
1713 /**
1714  * Send a SESSION_CONNECT message via the given address.
1715  *
1716  * @param na address to use
1717  */
1718 static void
1719 send_session_connect (struct NeighbourAddress *na)
1720 {
1721   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1722   struct SessionConnectMessage connect_msg;
1723
1724   if (NULL == (papi = GST_plugins_find (na->address->transport_name)))  
1725   {
1726     GNUNET_break (0);
1727     return;
1728   }
1729   if (NULL == na->session)
1730     na->session = papi->get_session (papi->cls, na->address);    
1731   if (NULL == na->session)
1732   {
1733     GNUNET_break (0);
1734     return;
1735   }
1736   na->connect_timestamp = GNUNET_TIME_absolute_get ();
1737   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1738   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1739   connect_msg.reserved = htonl (0);
1740   connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1741   (void) papi->send (papi->cls,
1742                      na->session,
1743                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1744                      UINT_MAX,
1745                      GNUNET_TIME_UNIT_FOREVER_REL,
1746                      NULL, NULL);
1747
1748 }
1749
1750
1751 /**
1752  * Send a SESSION_CONNECT_ACK message via the given address.
1753  *
1754  * @param address address to use
1755  * @param session session to use
1756  * @param timestamp timestamp to use for the ACK message
1757  */
1758 static void
1759 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1760                                   struct Session *session,
1761                                   struct GNUNET_TIME_Absolute timestamp)
1762 {
1763   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1764   struct SessionConnectMessage connect_msg;
1765
1766   if (NULL == (papi = GST_plugins_find (address->transport_name)))  
1767   {
1768     GNUNET_break (0);
1769     return;
1770   }
1771   if (NULL == session)
1772     session = papi->get_session (papi->cls, address);    
1773   if (NULL == session)
1774   {
1775     GNUNET_break (0);
1776     return;
1777   }
1778   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1779   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1780   connect_msg.reserved = htonl (0);
1781   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1782   (void) papi->send (papi->cls,
1783                      session,
1784                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1785                      UINT_MAX,
1786                      GNUNET_TIME_UNIT_FOREVER_REL,
1787                      NULL, NULL);
1788
1789 }
1790
1791
1792 /**
1793  * Create a fresh entry in the neighbour map for the given peer
1794  *
1795  * @param peer peer to create an entry for
1796  * @return new neighbour map entry
1797  */
1798 static struct NeighbourMapEntry *
1799 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1800 {
1801   struct NeighbourMapEntry *n;
1802
1803   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1804               "Creating new neighbour entry for `%s'\n", 
1805               GNUNET_i2s (peer));
1806   n = MEMDEBUG_malloc (sizeof (struct NeighbourMapEntry), __LINE__);
1807   n->id = *peer;
1808   n->state = S_NOT_CONNECTED;
1809   n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1810   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1811                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1812                                  MAX_BANDWIDTH_CARRY_S);
1813   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1814   GNUNET_assert (GNUNET_OK ==
1815                  GNUNET_CONTAINER_multihashmap_put (neighbours,
1816                                                     &n->id.hashPubKey, n,
1817                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1818   return n;
1819 }
1820
1821
1822 /**
1823  * Check if the two given addresses are the same.
1824  * Actually only checks if the sessions are non-NULL
1825  * (which they should be) and then if they are identical;
1826  * the actual addresses don't matter if the session
1827  * pointers match anyway, and we must have session pointers
1828  * at this time.
1829  *
1830  * @param a1 first address to compare
1831  * @param a2 other address to compare
1832  * @return GNUNET_NO if the addresses do not match, GNUNET_YES if they do match
1833  */
1834 static int
1835 address_matches (const struct NeighbourAddress *a1,
1836                  const struct NeighbourAddress *a2)
1837 {
1838   if ( (NULL == a1->session) ||
1839        (NULL == a2->session) )
1840   {
1841     GNUNET_break (0);
1842     return 0;
1843   }
1844   return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1845 }
1846
1847
1848 /**
1849  * Try to create a connection to the given target (eventually).
1850  *
1851  * @param target peer to try to connect to
1852  */
1853 void
1854 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1855 {
1856   struct NeighbourMapEntry *n;
1857
1858   if (NULL == neighbours)  
1859     return; /* during shutdown, do nothing */
1860   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
1861               "Asked to connect to peer `%s'\n",
1862               GNUNET_i2s (target));
1863   if (0 ==
1864       memcmp (target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1865   {
1866     /* refuse to connect to myself */
1867     /* FIXME: can this happen? Is this not an API violation? */
1868     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1869                 "Refusing to try to connect to myself.\n");
1870     return;
1871   }
1872   n = lookup_neighbour (target);
1873   if (NULL != n)
1874   {
1875     switch (n->state)
1876     {
1877     case S_NOT_CONNECTED:
1878       /* this should not be possible */
1879       GNUNET_break (0);
1880       free_neighbour (n, GNUNET_NO);
1881       break;
1882     case S_INIT_ATS:
1883     case S_INIT_BLACKLIST:
1884     case S_CONNECT_SENT:
1885     case S_CONNECT_RECV_BLACKLIST_INBOUND:
1886     case S_CONNECT_RECV_ATS:
1887     case S_CONNECT_RECV_BLACKLIST:
1888     case S_CONNECT_RECV_ACK:
1889       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1890                   "Ignoring request to try to connect to `%s', already trying!\n",
1891                   GNUNET_i2s (target));
1892       return; /* already trying */
1893     case S_CONNECTED:      
1894     case S_RECONNECT_ATS:
1895     case S_RECONNECT_BLACKLIST:
1896     case S_RECONNECT_SENT:
1897     case S_CONNECTED_SWITCHING_BLACKLIST:
1898     case S_CONNECTED_SWITCHING_CONNECT_SENT:
1899       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1900                   "Ignoring request to try to connect, already connected to `%s'!\n",
1901                   GNUNET_i2s (target));
1902       return; /* already connected */
1903     case S_DISCONNECT:
1904       /* get rid of remains, ready to re-try immediately */
1905       free_neighbour (n, GNUNET_NO);
1906       break;
1907     case S_DISCONNECT_FINISHED:
1908       /* should not be possible */      
1909       GNUNET_assert (0); 
1910     default:
1911       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
1912       GNUNET_break (0);
1913       free_neighbour (n, GNUNET_NO);
1914       break;
1915     }
1916   }
1917   n = setup_neighbour (target);  
1918   n->state = S_INIT_ATS; 
1919   n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1920
1921   GNUNET_ATS_reset_backoff (GST_ats, target);
1922   n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1923 }
1924
1925
1926 /**
1927  * Function called with the result of a blacklist check.
1928  *
1929  * @param cls closure with the 'struct BlackListCheckContext'
1930  * @param peer peer this check affects
1931  * @param result GNUNET_OK if the address is allowed
1932  */
1933 static void
1934 handle_test_blacklist_cont (void *cls,
1935                             const struct GNUNET_PeerIdentity *peer,
1936                             int result)
1937 {
1938   struct BlackListCheckContext *bcc = cls;
1939   struct NeighbourMapEntry *n;
1940
1941   bcc->bc = NULL;
1942   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1943               "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1944               GNUNET_i2s (peer),
1945               (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1946   if (NULL == (n = lookup_neighbour (peer)))
1947     goto cleanup; /* nobody left to care about new address */
1948   switch (n->state)
1949   {
1950   case S_NOT_CONNECTED:
1951     /* this should not be possible */
1952     GNUNET_break (0);
1953     free_neighbour (n, GNUNET_NO);
1954     break;
1955   case S_INIT_ATS:
1956     /* still waiting on ATS suggestion */
1957     break;
1958   case S_INIT_BLACKLIST:
1959     /* check if the address the blacklist was fine with matches
1960        ATS suggestion, if so, we can move on! */
1961     if ( (GNUNET_OK == result) &&
1962          (1 == n->send_connect_ack) )
1963     {
1964       n->send_connect_ack = 2;
1965       send_session_connect_ack_message (bcc->na.address,
1966                                         bcc->na.session,
1967                                         n->connect_ack_timestamp);
1968     }
1969     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1970       break; /* result for an address we currently don't care about */
1971     if (GNUNET_OK == result)
1972     {
1973       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1974       n->state = S_CONNECT_SENT;
1975       send_session_connect (&n->primary_address);
1976     }
1977     else
1978     {
1979       // FIXME: should also possibly destroy session with plugin!?
1980       GNUNET_ATS_address_destroyed (GST_ats,
1981                                     bcc->na.address,
1982                                     NULL);
1983       free_address (&n->primary_address);
1984       n->state = S_INIT_ATS;
1985       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1986       // FIXME: do we need to ask ATS again for suggestions?
1987       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1988     }
1989     break;
1990   case S_CONNECT_SENT:
1991     /* waiting on CONNECT_ACK, send ACK if one is pending */
1992     if ( (GNUNET_OK == result) &&
1993          (1 == n->send_connect_ack) )
1994     {
1995       n->send_connect_ack = 2;
1996       send_session_connect_ack_message (n->primary_address.address,
1997                                         n->primary_address.session,
1998                                         n->connect_ack_timestamp);
1999     }
2000     break; 
2001   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2002     if (GNUNET_OK == result)
2003     {
2004       /* valid new address, let ATS know! */
2005       GNUNET_ATS_address_add (GST_ats,
2006                               bcc->na.address,
2007                               bcc->na.session,
2008                               bcc->ats, bcc->ats_count);
2009     }
2010     n->state = S_CONNECT_RECV_ATS;
2011     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2012     GNUNET_ATS_reset_backoff (GST_ats, peer);
2013     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2014     break;
2015   case S_CONNECT_RECV_ATS:
2016     /* still waiting on ATS suggestion, don't care about blacklist */
2017     break;
2018   case S_CONNECT_RECV_BLACKLIST:
2019     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2020       break; /* result for an address we currently don't care about */
2021     if (GNUNET_OK == result)
2022     {
2023       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
2024       n->state = S_CONNECT_RECV_ACK;
2025       send_session_connect_ack_message (bcc->na.address,
2026                                         bcc->na.session,
2027                                         n->connect_ack_timestamp);
2028       if (1 == n->send_connect_ack) 
2029         n->send_connect_ack = 2;
2030     }
2031     else
2032     {
2033       // FIXME: should also possibly destroy session with plugin!?
2034       GNUNET_ATS_address_destroyed (GST_ats,
2035                                     bcc->na.address,
2036                                     NULL);
2037       free_address (&n->primary_address);
2038       n->state = S_INIT_ATS;
2039       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2040       // FIXME: do we need to ask ATS again for suggestions?
2041       GNUNET_ATS_reset_backoff (GST_ats, peer);
2042       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2043     }
2044     break;
2045   case S_CONNECT_RECV_ACK:
2046     /* waiting on SESSION_ACK, send ACK if one is pending */
2047     if ( (GNUNET_OK == result) &&
2048          (1 == n->send_connect_ack) )
2049     {
2050       n->send_connect_ack = 2;
2051       send_session_connect_ack_message (n->primary_address.address,
2052                                         n->primary_address.session,
2053                                         n->connect_ack_timestamp);
2054     }
2055     break; 
2056   case S_CONNECTED:
2057     /* already connected, don't care about blacklist */
2058     break;
2059   case S_RECONNECT_ATS:
2060     /* still waiting on ATS suggestion, don't care about blacklist */
2061     break;     
2062   case S_RECONNECT_BLACKLIST:
2063     if ( (GNUNET_OK == result) &&
2064          (1 == n->send_connect_ack) )
2065     {
2066       n->send_connect_ack = 2;
2067       send_session_connect_ack_message (bcc->na.address,
2068                                         bcc->na.session,
2069                                         n->connect_ack_timestamp);
2070     }
2071     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2072       break; /* result for an address we currently don't care about */
2073     if (GNUNET_OK == result)
2074     {
2075       send_session_connect (&n->primary_address);
2076       n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2077       n->state = S_RECONNECT_SENT;
2078     }
2079     else
2080     {
2081       GNUNET_ATS_address_destroyed (GST_ats,
2082                                     bcc->na.address,
2083                                     NULL);
2084       n->state = S_RECONNECT_ATS;
2085       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2086       // FIXME: do we need to ask ATS again for suggestions?
2087       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2088     }
2089     break;
2090   case S_RECONNECT_SENT:
2091     /* waiting on CONNECT_ACK, don't care about blacklist */
2092     if ( (GNUNET_OK == result) &&
2093          (1 == n->send_connect_ack) )
2094     {
2095       n->send_connect_ack = 2;
2096       send_session_connect_ack_message (n->primary_address.address,
2097                                         n->primary_address.session,
2098                                         n->connect_ack_timestamp);
2099     }
2100     break;     
2101   case S_CONNECTED_SWITCHING_BLACKLIST:
2102     if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2103       break; /* result for an address we currently don't care about */
2104     if (GNUNET_OK == result)
2105     {
2106       send_session_connect (&n->alternative_address);
2107       n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
2108     }
2109     else
2110     {
2111       GNUNET_ATS_address_destroyed (GST_ats,
2112                                     bcc->na.address,
2113                                     NULL);
2114       free_address (&n->alternative_address);
2115       n->state = S_CONNECTED;
2116     }
2117     break;
2118   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2119     /* waiting on CONNECT_ACK, don't care about blacklist */
2120     if ( (GNUNET_OK == result) &&
2121          (1 == n->send_connect_ack) )
2122     {
2123       n->send_connect_ack = 2;
2124       send_session_connect_ack_message (n->primary_address.address,
2125                                         n->primary_address.session,
2126                                         n->connect_ack_timestamp);
2127     }
2128     break;     
2129   case S_DISCONNECT:
2130     /* Nothing to do here, ATS will already do what can be done */
2131     break;
2132   case S_DISCONNECT_FINISHED:
2133     /* should not be possible */
2134     GNUNET_assert (0);
2135     break;
2136   default:
2137     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2138     GNUNET_break (0);
2139     free_neighbour (n, GNUNET_NO);
2140     break;
2141   }
2142  cleanup:
2143   MEMDEBUG_free (bcc->na.address, __LINE__);
2144   //GNUNET_HELLO_address_free (bcc->na.address);
2145   GNUNET_CONTAINER_DLL_remove (bc_head,
2146                                bc_tail,
2147                                bcc);
2148   MEMDEBUG_free (bcc, __LINE__);
2149 }
2150
2151
2152 /**
2153  * We want to know if connecting to a particular peer via
2154  * a particular address is allowed.  Check it!
2155  *
2156  * @param peer identity of the peer to switch the address for
2157  * @param ts time at which the check was initiated
2158  * @param address address of the other peer, NULL if other peer
2159  *                       connected to us
2160  * @param session session to use (or NULL)
2161  * @param ats performance data
2162  * @param ats_count number of entries in ats (excluding 0-termination)
2163  */
2164 static void
2165 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2166                  struct GNUNET_TIME_Absolute ts,
2167                  const struct GNUNET_HELLO_Address *address,
2168                  struct Session *session,
2169                  const struct GNUNET_ATS_Information *ats,
2170                  uint32_t ats_count)
2171 {
2172   struct BlackListCheckContext *bcc;
2173   struct GST_BlacklistCheck *bc;
2174
2175   bcc =
2176       MEMDEBUG_malloc (sizeof (struct BlackListCheckContext) +
2177                      sizeof (struct GNUNET_ATS_Information) * ats_count, __LINE__);
2178   bcc->ats_count = ats_count;
2179   bcc->na.address = GNUNET_HELLO_address_copy (address);
2180   MEMDEBUG_add_alloc (bcc->na.address, GNUNET_HELLO_address_get_size (address), __LINE__);
2181   bcc->na.session = session;
2182   bcc->na.connect_timestamp = ts;
2183   bcc->ats = (struct GNUNET_ATS_Information *) &bcc[1];
2184   memcpy (bcc->ats, ats, sizeof (struct GNUNET_ATS_Information) * ats_count);
2185   GNUNET_CONTAINER_DLL_insert (bc_head,
2186                                bc_tail,
2187                                bcc);
2188   if (NULL != (bc = GST_blacklist_test_allowed (peer, 
2189                                                 address->transport_name,
2190                                                 &handle_test_blacklist_cont, bcc)))
2191     bcc->bc = bc; 
2192   /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2193      we must only store 'bc' if 'bc' is non-NULL... */
2194 }
2195
2196
2197 /**
2198  * We received a 'SESSION_CONNECT' message from the other peer.
2199  * Consider switching to it.
2200  *
2201  * @param message possibly a 'struct SessionConnectMessage' (check format)
2202  * @param peer identity of the peer to switch the address for
2203  * @param address address of the other peer, NULL if other peer
2204  *                       connected to us
2205  * @param session session to use (or NULL)
2206  * @param ats performance data
2207  * @param ats_count number of entries in ats (excluding 0-termination)
2208  */
2209 void
2210 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2211                                const struct GNUNET_PeerIdentity *peer,
2212                                const struct GNUNET_HELLO_Address *address,
2213                                struct Session *session,
2214                                const struct GNUNET_ATS_Information *ats,
2215                                uint32_t ats_count)
2216 {
2217   const struct SessionConnectMessage *scm;
2218   struct NeighbourMapEntry *n;
2219   struct GNUNET_TIME_Absolute ts;
2220
2221   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2222               "Received CONNECT message from peer `%s'\n", 
2223               GNUNET_i2s (peer));
2224
2225   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2226   {
2227     GNUNET_break_op (0);
2228     return;
2229   }
2230   if (NULL == neighbours)
2231     return; /* we're shutting down */
2232   scm = (const struct SessionConnectMessage *) message;
2233   GNUNET_break_op (0 == ntohl (scm->reserved));
2234   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2235   n = lookup_neighbour (peer);
2236   if (NULL == n)
2237     n = setup_neighbour (peer);
2238   n->send_connect_ack = 1;
2239   n->connect_ack_timestamp = ts;
2240
2241   switch (n->state)
2242   {  
2243   case S_NOT_CONNECTED:
2244     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2245     /* Do a blacklist check for the new address */
2246     check_blacklist (peer, ts, address, session, ats, ats_count);
2247     break;
2248   case S_INIT_ATS:
2249     /* CONNECT message takes priority over us asking ATS for address */
2250     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2251     /* fallthrough */
2252   case S_INIT_BLACKLIST:
2253   case S_CONNECT_SENT:
2254   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2255   case S_CONNECT_RECV_ATS:
2256   case S_CONNECT_RECV_BLACKLIST:
2257   case S_CONNECT_RECV_ACK:
2258     /* It can never hurt to have an alternative address in the above cases, 
2259        see if it is allowed */
2260     check_blacklist (peer, ts, address, session, ats, ats_count);
2261     break;
2262   case S_CONNECTED:
2263     /* we are already connected and can thus send the ACK immediately;
2264        still, it can never hurt to have an alternative address, so also
2265        tell ATS  about it */
2266     GNUNET_assert (NULL != n->primary_address.address);
2267     GNUNET_assert (NULL != n->primary_address.session);
2268     n->send_connect_ack = 0;
2269     send_session_connect_ack_message (n->primary_address.address,
2270                                       n->primary_address.session, ts);
2271     check_blacklist (peer, ts, address, session, ats, ats_count);
2272     break;
2273   case S_RECONNECT_ATS:
2274   case S_RECONNECT_BLACKLIST:
2275   case S_RECONNECT_SENT:
2276     /* It can never hurt to have an alternative address in the above cases, 
2277        see if it is allowed */
2278     check_blacklist (peer, ts, address, session, ats, ats_count);
2279     break;
2280   case S_CONNECTED_SWITCHING_BLACKLIST:
2281   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2282     /* we are already connected and can thus send the ACK immediately;
2283        still, it can never hurt to have an alternative address, so also
2284        tell ATS  about it */
2285     GNUNET_assert (NULL != n->primary_address.address);
2286     GNUNET_assert (NULL != n->primary_address.session);
2287     n->send_connect_ack = 0;
2288     send_session_connect_ack_message (n->primary_address.address,
2289                                       n->primary_address.session, ts);
2290     check_blacklist (peer, ts, address, session, ats, ats_count);
2291     break;
2292   case S_DISCONNECT:
2293     /* get rid of remains without terminating sessions, ready to re-try */
2294     free_neighbour (n, GNUNET_YES);
2295     n = setup_neighbour (peer);
2296     n->state = S_CONNECT_RECV_ATS;
2297     GNUNET_ATS_reset_backoff (GST_ats, peer);
2298     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2299     break;
2300   case S_DISCONNECT_FINISHED:
2301     /* should not be possible */
2302     GNUNET_assert (0);
2303     break;
2304   default:
2305     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2306     GNUNET_break (0);
2307     free_neighbour (n, GNUNET_NO);
2308     break;
2309   }
2310 }
2311
2312
2313 /**
2314  * For an existing neighbour record, set the active connection to
2315  * use the given address.  
2316  *
2317  * @param peer identity of the peer to switch the address for
2318  * @param address address of the other peer, NULL if other peer
2319  *                       connected to us
2320  * @param session session to use (or NULL)
2321  * @param ats performance data
2322  * @param ats_count number of entries in ats
2323  * @param bandwidth_in inbound quota to be used when connection is up
2324  * @param bandwidth_out outbound quota to be used when connection is up
2325  */
2326 void
2327 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2328                                   const struct GNUNET_HELLO_Address *address,
2329                                   struct Session *session,
2330                                   const struct GNUNET_ATS_Information *ats,
2331                                   uint32_t ats_count,
2332                                   struct GNUNET_BANDWIDTH_Value32NBO
2333                                   bandwidth_in,
2334                                   struct GNUNET_BANDWIDTH_Value32NBO
2335                                   bandwidth_out)
2336 {
2337   struct NeighbourMapEntry *n;
2338   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2339
2340   GNUNET_assert (address->transport_name != NULL);
2341   if (NULL == (n = lookup_neighbour (peer)))
2342     return;
2343
2344   /* Obtain an session for this address from plugin */
2345   if (NULL == (papi = GST_plugins_find (address->transport_name)))
2346   {
2347     /* we don't have the plugin for this address */
2348           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2349                       "2348 : `%s' \n", address->transport_name);
2350     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2351     return;
2352   }
2353   if ((NULL == session) && (0 == address->address_length))
2354   {
2355     GNUNET_break (0);
2356     if (strlen (address->transport_name) > 0)
2357       GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2358     return;
2359   }
2360
2361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2362               "ATS tells us to switch to address '%s' session %p for "
2363               "peer `%s' in state %s (quota in/out %u %u )\n",
2364               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2365               session,
2366               GNUNET_i2s (peer),
2367               print_state (n->state),
2368               ntohl (bandwidth_in.value__),
2369               ntohl (bandwidth_out.value__));
2370
2371   if (NULL == session)
2372   {
2373     session = papi->get_session (papi->cls, address);
2374     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2375                 "Obtained new session for peer `%s' and  address '%s': %p\n",
2376                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2377   }
2378   if (NULL == session)
2379   {
2380     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2381                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
2382                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));    
2383     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2384     return;
2385   }
2386   switch (n->state)
2387   {
2388   case S_NOT_CONNECTED:
2389     GNUNET_break (0);
2390     free_neighbour (n, GNUNET_NO);
2391     return;
2392   case S_INIT_ATS:
2393     set_address (&n->primary_address,
2394                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2395     n->state = S_INIT_BLACKLIST;
2396     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2397     check_blacklist (&n->id,
2398                      n->connect_ack_timestamp,
2399                      address, session, ats, ats_count);    
2400     break;
2401   case S_INIT_BLACKLIST:
2402     /* ATS suggests a different address, switch again */
2403     set_address (&n->primary_address,
2404                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2405     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2406     check_blacklist (&n->id,
2407                      n->connect_ack_timestamp,
2408                      address, session, ats, ats_count);    
2409     break;
2410   case S_CONNECT_SENT:
2411     /* ATS suggests a different address, switch again */
2412     set_address (&n->primary_address,
2413                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2414     n->state = S_INIT_BLACKLIST;
2415     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2416     check_blacklist (&n->id,
2417                      n->connect_ack_timestamp,
2418                      address, session, ats, ats_count);    
2419     break;
2420   case S_CONNECT_RECV_ATS:
2421     set_address (&n->primary_address,
2422                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2423     n->state = S_CONNECT_RECV_BLACKLIST;
2424     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2425     check_blacklist (&n->id,
2426                      n->connect_ack_timestamp,
2427                      address, session, ats, ats_count);    
2428     break;
2429   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2430     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2431     check_blacklist (&n->id,
2432                      n->connect_ack_timestamp,
2433                      address, session, ats, ats_count);
2434     break;
2435   case S_CONNECT_RECV_BLACKLIST:
2436   case S_CONNECT_RECV_ACK:
2437     /* ATS asks us to switch while we were trying to connect; switch to new
2438        address and check blacklist again */
2439     set_address (&n->primary_address,
2440                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2441     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2442     check_blacklist (&n->id,
2443                      n->connect_ack_timestamp,
2444                      address, session, ats, ats_count);    
2445     break;
2446   case S_CONNECTED:
2447     GNUNET_assert (NULL != n->primary_address.address);
2448     GNUNET_assert (NULL != n->primary_address.session);
2449     if (n->primary_address.session == session)
2450     {
2451       /* not an address change, just a quota change */
2452       set_address (&n->primary_address,
2453                    address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2454       break;
2455     }
2456     /* ATS asks us to switch a life connection; see if we can get
2457        a CONNECT_ACK on it before we actually do this! */
2458     set_address (&n->alternative_address,
2459                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2460     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2461     check_blacklist (&n->id,
2462                      GNUNET_TIME_absolute_get (),
2463                      address, session, ats, ats_count);
2464     break;
2465   case S_RECONNECT_ATS:
2466     set_address (&n->primary_address,
2467                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2468     n->state = S_RECONNECT_BLACKLIST;
2469     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2470     check_blacklist (&n->id,
2471                      n->connect_ack_timestamp,
2472                      address, session, ats, ats_count);    
2473     break;
2474   case S_RECONNECT_BLACKLIST:
2475     /* ATS asks us to switch while we were trying to reconnect; switch to new
2476        address and check blacklist again */
2477     set_address (&n->primary_address,
2478                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2479     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2480     check_blacklist (&n->id,
2481                      n->connect_ack_timestamp,
2482                      address, session, ats, ats_count);    
2483     break;
2484   case S_RECONNECT_SENT:
2485     /* ATS asks us to switch while we were trying to reconnect; switch to new
2486        address and check blacklist again */
2487     set_address (&n->primary_address,
2488                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2489     n->state = S_RECONNECT_BLACKLIST;
2490     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2491     check_blacklist (&n->id,
2492                      n->connect_ack_timestamp,
2493                      address, session, ats, ats_count); 
2494     break;
2495   case S_CONNECTED_SWITCHING_BLACKLIST:
2496     if (n->primary_address.session == session)
2497     {
2498       /* ATS switches back to still-active session */
2499       free_address (&n->alternative_address);
2500       n->state = S_CONNECTED;
2501       break;
2502     }
2503     /* ATS asks us to switch a life connection, update blacklist check */
2504     set_address (&n->alternative_address,
2505                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2506     check_blacklist (&n->id,
2507                      GNUNET_TIME_absolute_get (),
2508                      address, session, ats, ats_count);
2509     break;
2510   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2511     if (n->primary_address.session == session)
2512     {
2513       /* ATS switches back to still-active session */
2514       free_address (&n->alternative_address);
2515       n->state = S_CONNECTED;
2516       break;
2517     }
2518     /* ATS asks us to switch a life connection, update blacklist check */
2519     set_address (&n->alternative_address,
2520                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2521     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2522     check_blacklist (&n->id,
2523                      GNUNET_TIME_absolute_get (),
2524                      address, session, ats, ats_count);
2525     break;
2526   case S_DISCONNECT:
2527     /* not going to switch addresses while disconnecting */
2528     return;
2529   case S_DISCONNECT_FINISHED:
2530     GNUNET_assert (0);
2531     break;
2532   default:
2533     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2534     GNUNET_break (0);
2535     break;
2536   }
2537 }
2538
2539
2540 /**
2541  * Master task run for every neighbour.  Performs all of the time-related
2542  * activities (keep alive, send next message, disconnect if idle, finish
2543  * clean up after disconnect).
2544  *
2545  * @param cls the 'struct NeighbourMapEntry' for which we are running
2546  * @param tc scheduler context (unused)
2547  */
2548 static void
2549 master_task (void *cls,
2550              const struct GNUNET_SCHEDULER_TaskContext *tc)
2551 {
2552   struct NeighbourMapEntry *n = cls;
2553   struct GNUNET_TIME_Relative delay;
2554
2555   n->task = GNUNET_SCHEDULER_NO_TASK;
2556   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);  
2557   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2558               "Master task runs for neighbour `%s' in state %s with timeout in %llu ms\n",
2559               GNUNET_i2s (&n->id),
2560               print_state(n->state),
2561               (unsigned long long) delay.rel_value);
2562   switch (n->state)
2563   {
2564   case S_NOT_CONNECTED:
2565     /* invalid state for master task, clean up */
2566     GNUNET_break (0);
2567     n->state = S_DISCONNECT_FINISHED;
2568     free_neighbour (n, GNUNET_NO);
2569     return;
2570   case S_INIT_ATS:
2571     if (0 == delay.rel_value)
2572     {
2573       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2574                   "Connection to `%s' timed out waiting for ATS to provide address\n",
2575                   GNUNET_i2s (&n->id));
2576       n->state = S_DISCONNECT_FINISHED;
2577       free_neighbour (n, GNUNET_NO);
2578       return;
2579     }
2580     break;
2581   case S_INIT_BLACKLIST:
2582     if (0 == delay.rel_value)
2583     {
2584       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2585                   "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2586                   GNUNET_i2s (&n->id));
2587       n->state = S_DISCONNECT_FINISHED;
2588       free_neighbour (n, GNUNET_NO);
2589       return;
2590     }
2591     break;
2592   case S_CONNECT_SENT:
2593     if (0 == delay.rel_value)
2594     {
2595       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2596                   "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2597                   GNUNET_i2s (&n->id));
2598       disconnect_neighbour (n);
2599       return;
2600     }
2601     break;
2602   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2603     if (0 == delay.rel_value)
2604     {
2605       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2606                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2607                   GNUNET_i2s (&n->id));
2608       n->state = S_DISCONNECT_FINISHED;
2609       free_neighbour (n, GNUNET_NO);
2610       return;
2611     }
2612     break;
2613   case S_CONNECT_RECV_ATS:
2614     if (0 == delay.rel_value)
2615     {
2616       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2617                   "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2618                   GNUNET_i2s (&n->id));
2619       n->state = S_DISCONNECT_FINISHED;
2620       free_neighbour (n, GNUNET_NO);
2621       return;
2622     }
2623     break;
2624   case S_CONNECT_RECV_BLACKLIST:
2625     if (0 == delay.rel_value)
2626     {
2627       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2628                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2629                   GNUNET_i2s (&n->id));
2630       n->state = S_DISCONNECT_FINISHED;
2631       free_neighbour (n, GNUNET_NO);
2632       return;
2633     }
2634     break;
2635   case S_CONNECT_RECV_ACK:
2636     if (0 == delay.rel_value)
2637     {
2638       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2639                   "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2640                   GNUNET_i2s (&n->id));
2641       disconnect_neighbour (n);
2642       return;
2643     }
2644     break;
2645   case S_CONNECTED:
2646     if (0 == delay.rel_value)
2647     {
2648       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2649                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2650                   GNUNET_i2s (&n->id));
2651       disconnect_neighbour (n);
2652       return;
2653     }
2654     try_transmission_to_peer (n);
2655     send_keepalive (n);
2656     break;
2657   case S_RECONNECT_ATS:
2658     if (0 == delay.rel_value)
2659     {
2660       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2661                   "Connection to `%s' timed out, waiting for ATS replacement address\n",
2662                   GNUNET_i2s (&n->id));
2663       disconnect_neighbour (n);
2664       return;
2665     }
2666     break;
2667   case S_RECONNECT_BLACKLIST:
2668     if (0 == delay.rel_value)
2669     {
2670       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2671                   "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2672                   GNUNET_i2s (&n->id));
2673       disconnect_neighbour (n);
2674       return;
2675     }
2676     break;
2677   case S_RECONNECT_SENT:
2678     if (0 == delay.rel_value)
2679     {
2680       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2681                   "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2682                   GNUNET_i2s (&n->id));
2683       disconnect_neighbour (n);
2684       return;
2685     }
2686     break;
2687   case S_CONNECTED_SWITCHING_BLACKLIST:
2688     if (0 == delay.rel_value)
2689     {
2690       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2692                   GNUNET_i2s (&n->id));
2693       disconnect_neighbour (n);
2694       return;
2695     }
2696     try_transmission_to_peer (n);
2697     send_keepalive (n);
2698     break;
2699   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2700     if (0 == delay.rel_value)
2701     {
2702       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2703                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2704                   GNUNET_i2s (&n->id));
2705       disconnect_neighbour (n);
2706       return;
2707     }
2708     try_transmission_to_peer (n);
2709     send_keepalive (n);
2710     break;
2711   case S_DISCONNECT:
2712     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2713                 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2714                 GNUNET_i2s (&n->id));
2715     free_neighbour (n, GNUNET_NO);
2716     return;
2717   case S_DISCONNECT_FINISHED:
2718     /* how did we get here!? */
2719     GNUNET_assert (0);
2720     break;
2721   default:
2722     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2723     GNUNET_break (0);
2724     break;  
2725   }
2726   if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2727        (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2728        (S_CONNECTED == n->state) )    
2729   {
2730     /* if we are *now* in one of these three states, we're sending
2731        keep alive messages, so we need to consider the keepalive
2732        delay, not just the connection timeout */
2733     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2734                                       delay);
2735   }
2736   if (GNUNET_SCHEDULER_NO_TASK == n->task)
2737     n->task = GNUNET_SCHEDULER_add_delayed (delay,
2738                                             &master_task,
2739                                             n);
2740 }
2741
2742
2743 /**
2744  * Send a SESSION_ACK message to the neighbour to confirm that we
2745  * got his CONNECT_ACK.
2746  *
2747  * @param n neighbour to send the SESSION_ACK to
2748  */
2749 static void
2750 send_session_ack_message (struct NeighbourMapEntry *n)
2751 {
2752   struct GNUNET_MessageHeader msg;
2753
2754   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2755   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2756   (void) send_with_session(n,
2757                            (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2758                            UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2759                            NULL, NULL);
2760 }
2761
2762
2763 /**
2764  * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2765  * Consider switching to it.
2766  *
2767  * @param message possibly a 'struct SessionConnectMessage' (check format)
2768  * @param peer identity of the peer to switch the address for
2769  * @param address address of the other peer, NULL if other peer
2770  *                       connected to us
2771  * @param session session to use (or NULL)
2772  * @param ats performance data
2773  * @param ats_count number of entries in ats
2774  */
2775 void
2776 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2777                                    const struct GNUNET_PeerIdentity *peer,
2778                                    const struct GNUNET_HELLO_Address *address,
2779                                    struct Session *session,
2780                                    const struct GNUNET_ATS_Information *ats,
2781                                    uint32_t ats_count)
2782 {
2783   const struct SessionConnectMessage *scm;
2784   struct GNUNET_TIME_Absolute ts;
2785   struct NeighbourMapEntry *n;
2786
2787   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2788               "Received CONNECT_ACK message from peer `%s'\n",
2789               GNUNET_i2s (peer));
2790
2791   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2792   {
2793     GNUNET_break_op (0);
2794     return;
2795   }
2796   scm = (const struct SessionConnectMessage *) message;
2797   GNUNET_break_op (ntohl (scm->reserved) == 0);
2798   if (NULL == (n = lookup_neighbour (peer)))
2799   {
2800     GNUNET_STATISTICS_update (GST_stats,
2801                               gettext_noop
2802                               ("# unexpected CONNECT_ACK messages (no peer)"),
2803                               1, GNUNET_NO);
2804     return;
2805   }
2806   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2807   switch (n->state)
2808   {
2809   case S_NOT_CONNECTED:
2810     GNUNET_break (0);
2811     free_neighbour (n, GNUNET_NO);
2812     return;
2813   case S_INIT_ATS:
2814   case S_INIT_BLACKLIST:
2815     GNUNET_STATISTICS_update (GST_stats,
2816                               gettext_noop
2817                               ("# unexpected CONNECT_ACK messages (not ready)"),
2818                               1, GNUNET_NO);
2819     break;    
2820   case S_CONNECT_SENT:
2821     if (ts.abs_value != n->primary_address.connect_timestamp.abs_value)
2822       break; /* ACK does not match our original CONNECT message */
2823     n->state = S_CONNECTED;
2824     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2825     GNUNET_STATISTICS_set (GST_stats, 
2826                            gettext_noop ("# peers connected"), 
2827                            ++neighbours_connected,
2828                            GNUNET_NO);
2829     connect_notify_cb (callback_cls, &n->id, ats, ats_count,
2830                        n->primary_address.bandwidth_in,
2831                        n->primary_address.bandwidth_out);
2832     /* Tell ATS that the outbound session we created to send CONNECT was successfull */
2833     GNUNET_ATS_address_add (GST_ats,
2834                             n->primary_address.address,
2835                             n->primary_address.session,
2836                             ats, ats_count);
2837     set_address (&n->primary_address,
2838                  n->primary_address.address,
2839                  n->primary_address.session,
2840                  n->primary_address.bandwidth_in,
2841                  n->primary_address.bandwidth_out,
2842                  GNUNET_YES);
2843     send_session_ack_message (n);
2844     break;
2845   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2846   case S_CONNECT_RECV_ATS:
2847   case S_CONNECT_RECV_BLACKLIST:
2848   case S_CONNECT_RECV_ACK:
2849     GNUNET_STATISTICS_update (GST_stats,
2850                               gettext_noop
2851                               ("# unexpected CONNECT_ACK messages (not ready)"),
2852                               1, GNUNET_NO);
2853     break;
2854   case S_CONNECTED:
2855     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2856     send_session_ack_message (n);
2857     break;
2858   case S_RECONNECT_ATS:
2859   case S_RECONNECT_BLACKLIST:
2860     /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2861        to give us a new address... */
2862     GNUNET_STATISTICS_update (GST_stats,
2863                               gettext_noop
2864                               ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2865                               1, GNUNET_NO);
2866     break;
2867   case S_RECONNECT_SENT:
2868     /* new address worked; go back to connected! */
2869     n->state = S_CONNECTED;
2870     send_session_ack_message (n);
2871     break;
2872   case S_CONNECTED_SWITCHING_BLACKLIST:
2873     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2874     send_session_ack_message (n);
2875     break;
2876   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2877     /* new address worked; adopt it and go back to connected! */
2878     n->state = S_CONNECTED;
2879     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2880     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2881     GNUNET_ATS_address_add(GST_ats,
2882                            n->alternative_address.address,
2883                            n->alternative_address.session,
2884                            ats, ats_count);
2885     set_address (&n->primary_address,
2886                  n->alternative_address.address,
2887                  n->alternative_address.session,
2888                  n->alternative_address.bandwidth_in,
2889                  n->alternative_address.bandwidth_out,
2890                  GNUNET_YES);
2891     free_address (&n->alternative_address);
2892     send_session_ack_message (n);
2893     break;    
2894   case S_DISCONNECT:
2895     GNUNET_STATISTICS_update (GST_stats,
2896                               gettext_noop
2897                               ("# unexpected CONNECT_ACK messages (disconnecting)"),
2898                               1, GNUNET_NO);
2899     break;
2900   case S_DISCONNECT_FINISHED:
2901     GNUNET_assert (0);
2902     break;
2903   default:
2904     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2905     GNUNET_break (0);
2906     break;   
2907   }
2908 }
2909
2910
2911 /**
2912  * A session was terminated. Take note; if needed, try to get
2913  * an alternative address from ATS.
2914  *
2915  * @param peer identity of the peer where the session died
2916  * @param session session that is gone
2917  * @return GNUNET_YES if this was a session used, GNUNET_NO if
2918  *        this session was not in use
2919  */
2920 int
2921 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2922                                    struct Session *session)
2923 {
2924   struct NeighbourMapEntry *n;
2925   struct BlackListCheckContext *bcc;
2926   struct BlackListCheckContext *bcc_next;
2927
2928   /* make sure to cancel all ongoing blacklist checks involving 'session' */
2929   bcc_next = bc_head;
2930   while (NULL != (bcc = bcc_next))
2931   {
2932     bcc_next = bcc->next;
2933     if (bcc->na.session == session)
2934     {
2935       GST_blacklist_test_cancel (bcc->bc);
2936       MEMDEBUG_free (bcc->na.address, __LINE__);
2937       //GNUNET_HELLO_address_free (bcc->na.address);
2938       GNUNET_CONTAINER_DLL_remove (bc_head,
2939                                    bc_tail,
2940                                    bcc);
2941       MEMDEBUG_free (bcc, __LINE__);
2942     }
2943   }
2944   if (NULL == (n = lookup_neighbour (peer)))
2945     return GNUNET_NO; /* can't affect us */
2946   if (session != n->primary_address.session)
2947   {
2948     if (session == n->alternative_address.session)
2949     {
2950       free_address (&n->alternative_address);
2951       if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2952            (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
2953         n->state = S_CONNECTED;
2954       else
2955         GNUNET_break (0);
2956     }
2957     return GNUNET_NO; /* doesn't affect us further */
2958   }
2959
2960   n->expect_latency_response = GNUNET_NO;
2961   switch (n->state)
2962   {
2963   case S_NOT_CONNECTED:
2964     GNUNET_break (0);
2965     free_neighbour (n, GNUNET_NO);
2966     return GNUNET_YES;
2967   case S_INIT_ATS:
2968     GNUNET_break (0);
2969     free_neighbour (n, GNUNET_NO);
2970     return GNUNET_YES;
2971   case S_INIT_BLACKLIST:
2972   case S_CONNECT_SENT:
2973     free_address (&n->primary_address);
2974     n->state = S_INIT_ATS;
2975     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2976     // FIXME: need to ask ATS for suggestions again?
2977     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2978     break;
2979   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2980   case S_CONNECT_RECV_ATS:    
2981   case S_CONNECT_RECV_BLACKLIST:
2982   case S_CONNECT_RECV_ACK:
2983     /* error on inbound session; free neighbour entirely */
2984     free_address (&n->primary_address);
2985     free_neighbour (n, GNUNET_NO);
2986     return GNUNET_YES;
2987   case S_CONNECTED:
2988     free_address (&n->primary_address);
2989     n->state = S_RECONNECT_ATS;
2990     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2991     /* FIXME: is this ATS call needed? */
2992     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2993     break;
2994   case S_RECONNECT_ATS:
2995     /* we don't have an address, how can it go down? */
2996     GNUNET_break (0);
2997     break;
2998   case S_RECONNECT_BLACKLIST:
2999   case S_RECONNECT_SENT:
3000     n->state = S_RECONNECT_ATS;
3001     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3002     // FIXME: need to ask ATS for suggestions again?
3003     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3004     break;
3005   case S_CONNECTED_SWITCHING_BLACKLIST:
3006     /* primary went down while we were checking secondary against
3007        blacklist, adopt secondary as primary */       
3008     free_address (&n->primary_address);
3009     n->primary_address = n->alternative_address;
3010     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3011     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3012     n->state = S_RECONNECT_BLACKLIST;
3013     break;
3014   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3015     /* primary went down while we were waiting for CONNECT_ACK on secondary;
3016        secondary as primary */       
3017     free_address (&n->primary_address);
3018     n->primary_address = n->alternative_address;
3019     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3020     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3021     n->state = S_RECONNECT_SENT;
3022     break;
3023   case S_DISCONNECT:
3024     free_address (&n->primary_address);
3025     break;
3026   case S_DISCONNECT_FINISHED:
3027     /* neighbour was freed and plugins told to terminate session */
3028     return GNUNET_NO;
3029     break;
3030   default:
3031     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
3032     GNUNET_break (0);
3033     break;
3034   }
3035   if (GNUNET_SCHEDULER_NO_TASK != n->task)
3036     GNUNET_SCHEDULER_cancel (n->task);
3037   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3038   return GNUNET_YES;
3039 }
3040
3041
3042 /**
3043  * We received a 'SESSION_ACK' message from the other peer.
3044  * If we sent a 'CONNECT_ACK' last, this means we are now
3045  * connected.  Otherwise, do nothing.
3046  *
3047  * @param message possibly a 'struct SessionConnectMessage' (check format)
3048  * @param peer identity of the peer to switch the address for
3049  * @param address address of the other peer, NULL if other peer
3050  *                       connected to us
3051  * @param session session to use (or NULL)
3052  * @param ats performance data
3053  * @param ats_count number of entries in ats
3054  */
3055 void
3056 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3057                                    const struct GNUNET_PeerIdentity *peer,
3058                                    const struct GNUNET_HELLO_Address *address,
3059                                    struct Session *session,
3060                                    const struct GNUNET_ATS_Information *ats,
3061                                    uint32_t ats_count)
3062 {
3063   struct NeighbourMapEntry *n;
3064
3065   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
3066               "Received SESSION_ACK message from peer `%s'\n",
3067               GNUNET_i2s (peer));
3068   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3069   {
3070     GNUNET_break_op (0);
3071     return;
3072   }
3073   if (NULL == (n = lookup_neighbour (peer)))
3074     return;
3075   /* check if we are in a plausible state for having sent
3076      a CONNECT_ACK.  If not, return, otherwise break */
3077   if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3078          (S_CONNECT_SENT != n->state) ) ||
3079        (2 != n->send_connect_ack) )
3080   {
3081     GNUNET_STATISTICS_update (GST_stats,
3082                               gettext_noop ("# unexpected SESSION ACK messages"), 1,
3083                               GNUNET_NO);
3084     return;
3085   }
3086   n->state = S_CONNECTED;
3087   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3088   GNUNET_STATISTICS_set (GST_stats, 
3089                          gettext_noop ("# peers connected"), 
3090                          ++neighbours_connected,
3091                          GNUNET_NO);
3092   connect_notify_cb (callback_cls, &n->id, ats, ats_count,
3093                      n->primary_address.bandwidth_in,
3094                      n->primary_address.bandwidth_out);
3095   GNUNET_ATS_address_add(GST_ats,
3096                          n->primary_address.address,
3097                          n->primary_address.session,
3098                          ats, ats_count);
3099   set_address (&n->primary_address,
3100                n->primary_address.address,
3101                n->primary_address.session,
3102                n->primary_address.bandwidth_in,
3103                n->primary_address.bandwidth_out,
3104                GNUNET_YES);
3105 }
3106
3107
3108 /**
3109  * Test if we're connected to the given peer.
3110  *
3111  * @param target peer to test
3112  * @return GNUNET_YES if we are connected, GNUNET_NO if not
3113  */
3114 int
3115 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3116 {
3117   return test_connected (lookup_neighbour (target));
3118 }
3119
3120
3121 /**
3122  * Change the incoming quota for the given peer.
3123  *
3124  * @param neighbour identity of peer to change qutoa for
3125  * @param quota new quota
3126  */
3127 void
3128 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3129                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
3130 {
3131   struct NeighbourMapEntry *n;
3132
3133   if (NULL == (n = lookup_neighbour (neighbour)))
3134   {
3135     GNUNET_STATISTICS_update (GST_stats,
3136                               gettext_noop
3137                               ("# SET QUOTA messages ignored (no such peer)"),
3138                               1, GNUNET_NO);
3139     return;
3140   }
3141   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3142               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3143               ntohl (quota.value__), GNUNET_i2s (&n->id));
3144   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3145   if (0 != ntohl (quota.value__))
3146     return;
3147   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3148               GNUNET_i2s (&n->id), "SET_QUOTA");
3149   if (GNUNET_YES == test_connected (n))
3150     GNUNET_STATISTICS_update (GST_stats,
3151                               gettext_noop ("# disconnects due to quota of 0"),
3152                               1, GNUNET_NO);
3153   disconnect_neighbour (n);
3154 }
3155
3156
3157 /**
3158  * We received a disconnect message from the given peer,
3159  * validate and process.
3160  *
3161  * @param peer sender of the message
3162  * @param msg the disconnect message
3163  */
3164 void
3165 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3166                                           *peer,
3167                                           const struct GNUNET_MessageHeader
3168                                           *msg)
3169 {
3170   struct NeighbourMapEntry *n;
3171   const struct SessionDisconnectMessage *sdm;
3172   struct GNUNET_HashCode hc;
3173
3174   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3175               "Received DISCONNECT message from peer `%s'\n",
3176               GNUNET_i2s (peer));
3177   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3178   {
3179     // GNUNET_break_op (0);
3180     GNUNET_STATISTICS_update (GST_stats,
3181                               gettext_noop
3182                               ("# disconnect messages ignored (old format)"), 1,
3183                               GNUNET_NO);
3184     return;
3185   }
3186   sdm = (const struct SessionDisconnectMessage *) msg;
3187   if (NULL == (n = lookup_neighbour (peer)))
3188     return;                     /* gone already */
3189   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value <= n->connect_ack_timestamp.abs_value)
3190   {
3191     GNUNET_STATISTICS_update (GST_stats,
3192                               gettext_noop
3193                               ("# disconnect messages ignored (timestamp)"), 1,
3194                               GNUNET_NO);
3195     return;
3196   }
3197   GNUNET_CRYPTO_hash (&sdm->public_key,
3198                       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
3199                       &hc);
3200   if (0 != memcmp (peer, &hc, sizeof (struct GNUNET_PeerIdentity)))
3201   {
3202     GNUNET_break_op (0);
3203     return;
3204   }
3205   if (ntohl (sdm->purpose.size) !=
3206       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3207       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
3208       sizeof (struct GNUNET_TIME_AbsoluteNBO))
3209   {
3210     GNUNET_break_op (0);
3211     return;
3212   }
3213   if (GNUNET_OK !=
3214       GNUNET_CRYPTO_ecc_verify
3215       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3216        &sdm->signature, &sdm->public_key))
3217   {
3218     GNUNET_break_op (0);
3219     return;
3220   }
3221   if (GNUNET_YES == test_connected (n))
3222     GNUNET_STATISTICS_update (GST_stats,
3223                               gettext_noop
3224                               ("# other peer asked to disconnect from us"), 1,
3225                               GNUNET_NO);
3226   disconnect_neighbour (n);
3227 }
3228
3229
3230 /**
3231  * Closure for the neighbours_iterate function.
3232  */
3233 struct IteratorContext
3234 {
3235   /**
3236    * Function to call on each connected neighbour.
3237    */
3238   GST_NeighbourIterator cb;
3239
3240   /**
3241    * Closure for 'cb'.
3242    */
3243   void *cb_cls;
3244 };
3245
3246
3247 /**
3248  * Call the callback from the closure for each connected neighbour.
3249  *
3250  * @param cls the 'struct IteratorContext'
3251  * @param key the hash of the public key of the neighbour
3252  * @param value the 'struct NeighbourMapEntry'
3253  * @return GNUNET_OK (continue to iterate)
3254  */
3255 static int
3256 neighbours_iterate (void *cls, const struct GNUNET_HashCode * key, void *value)
3257 {
3258   struct IteratorContext *ic = cls;
3259   struct NeighbourMapEntry *n = value;
3260
3261   if (GNUNET_YES == test_connected (n))
3262   {
3263     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3264     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3265
3266     if (NULL != n->primary_address.address)
3267     {
3268       bandwidth_in = n->primary_address.bandwidth_in;
3269       bandwidth_out = n->primary_address.bandwidth_out;
3270     }
3271     else
3272     {
3273       bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3274       bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3275     }
3276
3277     ic->cb (ic->cb_cls, &n->id, NULL, 0,
3278             n->primary_address.address,
3279             bandwidth_in, bandwidth_out);
3280   }
3281   return GNUNET_OK;
3282 }
3283
3284
3285 /**
3286  * Iterate over all connected neighbours.
3287  *
3288  * @param cb function to call
3289  * @param cb_cls closure for cb
3290  */
3291 void
3292 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3293 {
3294   struct IteratorContext ic;
3295
3296   if (NULL == neighbours)  
3297     return; /* can happen during shutdown */
3298   ic.cb = cb;
3299   ic.cb_cls = cb_cls;
3300   GNUNET_CONTAINER_multihashmap_iterate (neighbours, &neighbours_iterate, &ic);
3301 }
3302
3303
3304 /**
3305  * If we have an active connection to the given target, it must be shutdown.
3306  *
3307  * @param target peer to disconnect from
3308  */
3309 void
3310 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3311 {
3312   struct NeighbourMapEntry *n;
3313
3314   if (NULL == (n = lookup_neighbour (target)))
3315     return;  /* not active */
3316   if (GNUNET_YES == test_connected (n))
3317     GNUNET_STATISTICS_update (GST_stats,
3318                               gettext_noop
3319                               ("# disconnected from peer upon explicit request"), 1,
3320                               GNUNET_NO);
3321   disconnect_neighbour (n);
3322 }
3323
3324
3325 /**
3326  * Obtain current latency information for the given neighbour.
3327  *
3328  * @param peer to get the latency for
3329  * @return observed latency of the address, FOREVER if the 
3330  *         the connection is not up
3331  */
3332 struct GNUNET_TIME_Relative
3333 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3334 {
3335   struct NeighbourMapEntry *n;
3336
3337   n = lookup_neighbour (peer);
3338   if (NULL == n) 
3339     return GNUNET_TIME_UNIT_FOREVER_REL;
3340   switch (n->state)
3341   {
3342   case S_CONNECTED:
3343   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3344   case S_CONNECTED_SWITCHING_BLACKLIST:
3345   case S_RECONNECT_SENT:
3346   case S_RECONNECT_ATS:
3347   case S_RECONNECT_BLACKLIST:
3348     return n->latency;
3349   case S_NOT_CONNECTED:
3350   case S_INIT_BLACKLIST:
3351   case S_INIT_ATS:
3352   case S_CONNECT_RECV_BLACKLIST_INBOUND:
3353   case S_CONNECT_RECV_ATS:
3354   case S_CONNECT_RECV_BLACKLIST:
3355   case S_CONNECT_RECV_ACK:
3356   case S_CONNECT_SENT:
3357   case S_DISCONNECT:
3358   case S_DISCONNECT_FINISHED:
3359     return GNUNET_TIME_UNIT_FOREVER_REL;
3360   default:
3361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
3362     GNUNET_break (0);
3363     break;
3364   }
3365   return GNUNET_TIME_UNIT_FOREVER_REL;   
3366 }
3367
3368
3369 /**
3370  * Obtain current address information for the given neighbour.
3371  *
3372  * @param peer
3373  * @return address currently used
3374  */
3375 struct GNUNET_HELLO_Address *
3376 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3377 {
3378   struct NeighbourMapEntry *n;
3379
3380   n = lookup_neighbour (peer);
3381   if (NULL == n)
3382     return NULL;
3383   return n->primary_address.address;
3384 }
3385
3386
3387 /**
3388  * Initialize the neighbours subsystem.
3389  *
3390  * @param cls closure for callbacks
3391  * @param connect_cb function to call if we connect to a peer
3392  * @param disconnect_cb function to call if we disconnect from a peer
3393  * @param peer_address_cb function to call if we change an active address
3394  *                   of a neighbour
3395  * @param max_fds maximum number of fds to use
3396  */
3397 void
3398 GST_neighbours_start (void *cls,
3399                                                                         NotifyConnect connect_cb,
3400                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3401                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3402                       unsigned int max_fds)
3403 {
3404   callback_cls = cls;
3405   connect_notify_cb = connect_cb;
3406   disconnect_notify_cb = disconnect_cb;
3407   address_change_cb = peer_address_cb;
3408   neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3409 }
3410
3411
3412 /**
3413  * Disconnect from the given neighbour.
3414  *
3415  * @param cls unused
3416  * @param key hash of neighbour's public key (not used)
3417  * @param value the 'struct NeighbourMapEntry' of the neighbour
3418  * @return GNUNET_OK (continue to iterate)
3419  */
3420 static int
3421 disconnect_all_neighbours (void *cls, const struct GNUNET_HashCode * key, void *value)
3422 {
3423   struct NeighbourMapEntry *n = value;
3424
3425   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
3426               "Disconnecting peer `%4s', %s\n",
3427               GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3428   n->state = S_DISCONNECT_FINISHED;
3429   free_neighbour (n, GNUNET_NO);
3430   return GNUNET_OK;
3431 }
3432
3433
3434 /**
3435  * Cleanup the neighbours subsystem.
3436  */
3437 void
3438 GST_neighbours_stop ()
3439 {
3440   if (NULL == neighbours)
3441     return;
3442   GNUNET_CONTAINER_multihashmap_iterate (neighbours, 
3443                                          &disconnect_all_neighbours,
3444                                          NULL);
3445   GNUNET_CONTAINER_multihashmap_destroy (neighbours);
3446   neighbours = NULL;
3447   callback_cls = NULL;
3448   connect_notify_cb = NULL;
3449   disconnect_notify_cb = NULL;
3450   address_change_cb = NULL;
3451 }
3452
3453
3454 /* end of file gnunet-service-transport_neighbours.c */